Compare commits
106 Commits
wip/new-ap
...
3.9.91-way
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
c2bf44fa19 | ||
|
|
2d67b01c3a | ||
|
|
6014d31dea | ||
|
|
bf78f067a2 | ||
|
|
ddb9929dad | ||
|
|
2098ec2d16 | ||
|
|
97eece6607 | ||
|
|
7ac9a6e241 | ||
|
|
b53bf0e8c2 | ||
|
|
a26ded47d9 | ||
|
|
cad9e14463 | ||
|
|
806d5939e3 | ||
|
|
0f0c23fbab | ||
|
|
5d1fcc26c0 | ||
|
|
cbe5b6b3bc | ||
|
|
590cf4e832 | ||
|
|
22ca820c44 | ||
|
|
b1206ceb66 | ||
|
|
ad4053ab84 | ||
|
|
7bd4e6ecb0 | ||
|
|
deeb1db1ac | ||
|
|
7eb4bfbea3 | ||
|
|
59b274f12f | ||
|
|
e311cef013 | ||
|
|
e72f81c24f | ||
|
|
96fa518576 | ||
|
|
e263b3624c | ||
|
|
1c34f0b342 | ||
|
|
2d27873f98 | ||
|
|
fd40a12213 | ||
|
|
57406e0a9e | ||
|
|
1987cbb764 | ||
|
|
705978405b | ||
|
|
daba05f6a7 | ||
|
|
1a7984be43 | ||
|
|
1b3c77d53a | ||
|
|
5b0f0d9e5b | ||
|
|
9816659fa9 | ||
|
|
3b1b611634 | ||
|
|
f08921bd0c | ||
|
|
0089b5769c | ||
|
|
452be05ea0 | ||
|
|
69f038f7c7 | ||
|
|
2d35e07fae | ||
|
|
95e2d26d03 | ||
|
|
f506e090ea | ||
|
|
0697b53826 | ||
|
|
7d9141c56f | ||
|
|
519a06b93d | ||
|
|
0cceddab75 | ||
|
|
c9830c13b4 | ||
|
|
9a4783e364 | ||
|
|
aa15c09d54 | ||
|
|
62d908be42 | ||
|
|
6526e9882b | ||
|
|
ab72352c47 | ||
|
|
f09b9573f0 | ||
|
|
3d3ae40f79 | ||
|
|
ea3d2b4759 | ||
|
|
542a0886cf | ||
|
|
bd3d5df9ce | ||
|
|
91cdfab495 | ||
|
|
1617323dca | ||
|
|
bbf9358eba | ||
|
|
9682a2aea4 | ||
|
|
c9fbb51775 | ||
|
|
aa6d887214 | ||
|
|
13312527de | ||
|
|
9bd366f2a6 | ||
|
|
5de346bfef | ||
|
|
2af20b77b6 | ||
|
|
9affbf10a6 | ||
|
|
c0acf3ae6d | ||
|
|
2c1b20e15f | ||
|
|
77290b6736 | ||
|
|
876f81db12 | ||
|
|
75f3ae14b5 | ||
|
|
d26f248b0f | ||
|
|
eeb3dfc991 | ||
|
|
e66db2eab3 | ||
|
|
2ae7454f36 | ||
|
|
4fd3c63da9 | ||
|
|
bfc87d13cb | ||
|
|
24564c77d6 | ||
|
|
18a21b67c2 | ||
|
|
3803fd9511 | ||
|
|
152d896f75 | ||
|
|
2f3a5f2001 | ||
|
|
0e098249b1 | ||
|
|
9a5f243f73 | ||
|
|
03f55b9485 | ||
|
|
ef9ef87d91 | ||
|
|
0ee2c21da7 | ||
|
|
9b966561c4 | ||
|
|
8c0779a9db | ||
|
|
2c901cc015 | ||
|
|
85e66f69fa | ||
|
|
a5585327dc | ||
|
|
268ebb1b18 | ||
|
|
40e820f551 | ||
|
|
f9a11b3b18 | ||
|
|
bd3c357212 | ||
|
|
b4d108dac6 | ||
|
|
6585a5760b | ||
|
|
531be6c413 | ||
|
|
f0c503b5a9 |
19
.gitignore
vendored
19
.gitignore
vendored
@@ -23,7 +23,7 @@ src/50-mutter-navigation.xml
|
||||
src/50-mutter-system.xml
|
||||
src/50-mutter-windows.xml
|
||||
src/mutter-wm.desktop
|
||||
src/mutter.desktop
|
||||
src/mutter-wayland.desktop
|
||||
*.o
|
||||
*.a
|
||||
*.lo
|
||||
@@ -46,12 +46,13 @@ POTFILES
|
||||
po/*.pot
|
||||
50-metacity-desktop-key.xml
|
||||
50-metacity-key.xml
|
||||
libmutter.pc
|
||||
mutter
|
||||
mutter-theme-viewer
|
||||
mutter.desktop
|
||||
libmutter-wayland.pc
|
||||
mutter-wayland
|
||||
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
|
||||
@@ -74,7 +75,15 @@ src/mutter-enum-types.[ch]
|
||||
src/stamp-mutter-enum-types.h
|
||||
src/mutter-marshal.[ch]
|
||||
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
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
|
||||
SUBDIRS=src po doc
|
||||
SUBDIRS=src protocol data po doc
|
||||
|
||||
EXTRA_DIST = HACKING MAINTAINERS rationales.txt
|
||||
|
||||
|
||||
59
NEWS
59
NEWS
@@ -1,3 +1,62 @@
|
||||
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
|
||||
as a wayland compositor [Robert, Neil, Giovanni]
|
||||
* Add support for _GTK_FRAME_EXTENTS [Jasper; #705766]
|
||||
* Fix quick consecutive <super> presses breaking keyboard input [Alban; #666101]
|
||||
* Work towards running as wayland compositor [Giovanni]
|
||||
- Add DBus API for display configuration
|
||||
[#705670, #706231, #706233, #706322, #706382]
|
||||
- Add abstraction layer for cursor tracking [#705911]
|
||||
- Add support for plugin modality under wayland [#705917]
|
||||
* Disable GTK+ scaling [Alexander; #706388]
|
||||
* Disable blending while updating tower [Robert]
|
||||
* Misc bug fixes and cleanups [Adel, Jasper, Giovanni, Colin, Rico, Florian;
|
||||
#703332, #704437, #706207]
|
||||
|
||||
Contributors:
|
||||
Robert Bragg, Giovanni Campagna, Alban Crequy, Adel Gadllah,
|
||||
Alexander Larsson, Florian Müllner, Jasper St. Pierre, Neil Roberts,
|
||||
Rico Tzschichholz, Colin Walters
|
||||
|
||||
Translations:
|
||||
Jiro Matsuzawa [ja], Kjartan Maraas [nb], Matej Urbančič [sl],
|
||||
Marek Černocký [cs], Daniel Mustieles [es], Rafael Ferreira [pt_BR],
|
||||
Yaron Shahrabani [he], Ján Kyselica [sk]
|
||||
|
||||
3.9.5
|
||||
=====
|
||||
* Don't select for touch events on the stage [Jasper; #697192]
|
||||
|
||||
@@ -5,7 +5,7 @@ srcdir=`dirname $0`
|
||||
test -z "$srcdir" && srcdir=.
|
||||
|
||||
PKG_NAME="mutter"
|
||||
REQUIRED_AUTOMAKE_VERSION=1.10
|
||||
REQUIRED_AUTOMAKE_VERSION=1.13
|
||||
|
||||
(test -f $srcdir/configure.ac \
|
||||
&& test -d $srcdir/src) || {
|
||||
|
||||
48
configure.ac
48
configure.ac
@@ -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], [5])
|
||||
m4_define([mutter_micro_version], [91])
|
||||
|
||||
m4_define([mutter_version],
|
||||
[mutter_major_version.mutter_minor_version.mutter_micro_version])
|
||||
@@ -15,10 +15,14 @@ AC_INIT([mutter], [mutter_version],
|
||||
AC_CONFIG_SRCDIR(src/core/display.c)
|
||||
AC_CONFIG_HEADERS(config.h)
|
||||
|
||||
AM_INIT_AUTOMAKE([1.11 no-dist-gzip dist-xz tar-ustar])
|
||||
AM_INIT_AUTOMAKE([1.11 foreign no-dist-gzip dist-xz tar-ustar])
|
||||
m4_ifdef([AM_SILENT_RULES],[AM_SILENT_RULES([yes])],)
|
||||
AM_MAINTAINER_MODE([enable])
|
||||
|
||||
# Change pkglibdir and pkgdatadir to mutter-wayland instead of mutter
|
||||
PACKAGE="mutter-wayland"
|
||||
AC_SUBST([PACKAGE], [$PACKAGE])
|
||||
|
||||
MUTTER_MAJOR_VERSION=mutter_major_version
|
||||
MUTTER_MINOR_VERSION=mutter_minor_version
|
||||
MUTTER_MICRO_VERSION=mutter_micro_version
|
||||
@@ -34,7 +38,7 @@ AC_SUBST(MUTTER_PLUGIN_DIR)
|
||||
# Honor aclocal flags
|
||||
AC_SUBST(ACLOCAL_AMFLAGS, "\${ACLOCAL_FLAGS}")
|
||||
|
||||
GETTEXT_PACKAGE=mutter
|
||||
GETTEXT_PACKAGE=mutter-wayland
|
||||
AC_SUBST(GETTEXT_PACKAGE)
|
||||
AC_DEFINE_UNQUOTED(GETTEXT_PACKAGE,"$GETTEXT_PACKAGE",[Name of default gettext domain])
|
||||
|
||||
@@ -115,11 +119,32 @@ AC_ARG_ENABLE(shape,
|
||||
[disable mutter's use of the shaped window extension]),,
|
||||
enable_shape=auto)
|
||||
|
||||
## Wayland support requires the xserver.xml protocol extension found in the weston
|
||||
## repository but since there aren't currently established conventions for
|
||||
## installing and discovering these we simply require a location to be given
|
||||
## explicitly...
|
||||
AC_ARG_WITH([wayland-protocols],
|
||||
[AS_HELP_STRING([--with-wayland-protocols], [Location for wayland extension protocol specs])],
|
||||
[
|
||||
],
|
||||
[])
|
||||
|
||||
AC_ARG_WITH([xwayland-path],
|
||||
[AS_HELP_STRING([--with-xwayland-path], [Absolute path for an X Wayland server])],
|
||||
[XWAYLAND_PATH="$withval"],
|
||||
[XWAYLAND_PATH="$bindir/Xorg"])
|
||||
|
||||
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"
|
||||
@@ -198,6 +223,16 @@ if test x$have_xcursor = xyes; then
|
||||
AC_DEFINE(HAVE_XCURSOR, , [Building with Xcursor support])
|
||||
fi
|
||||
|
||||
# We always build with wayland enabled
|
||||
AC_DEFINE(HAVE_WAYLAND, , [Building with Wayland support])
|
||||
|
||||
AC_PATH_PROG([WAYLAND_SCANNER],[wayland-scanner],[no])
|
||||
AS_IF([test "x$WAYLAND_SCANNER" = "xno"],
|
||||
AC_MSG_ERROR([Could not find wayland-scanner in your PATH, required for parsing wayland extension protocols]))
|
||||
AC_SUBST([WAYLAND_SCANNER])
|
||||
AC_SUBST(XWAYLAND_PATH)
|
||||
|
||||
MUTTER_PC_MODULES="$MUTTER_PC_MODULES wayland-server libdrm"
|
||||
PKG_CHECK_MODULES(MUTTER, $MUTTER_PC_MODULES)
|
||||
|
||||
PKG_CHECK_EXISTS([xi >= 1.6.99.1],
|
||||
@@ -435,9 +470,10 @@ doc/man/Makefile
|
||||
doc/reference/Makefile
|
||||
doc/reference/meta-docs.sgml
|
||||
src/Makefile
|
||||
src/libmutter.pc
|
||||
src/mutter-plugins.pc
|
||||
src/libmutter-wayland.pc
|
||||
src/compositor/plugins/Makefile
|
||||
protocol/Makefile
|
||||
data/Makefile
|
||||
po/Makefile.in
|
||||
])
|
||||
|
||||
@@ -453,7 +489,7 @@ fi
|
||||
|
||||
dnl ==========================================================================
|
||||
echo "
|
||||
mutter-$VERSION
|
||||
mutter-wayland-$VERSION
|
||||
|
||||
prefix: ${prefix}
|
||||
source code location: ${srcdir}
|
||||
|
||||
3
data/Makefile.am
Normal file
3
data/Makefile.am
Normal file
@@ -0,0 +1,3 @@
|
||||
defaultcursordir = $(pkgdatadir)/cursors
|
||||
|
||||
dist_defaultcursor_DATA = left_ptr.png
|
||||
BIN
data/left_ptr.png
Normal file
BIN
data/left_ptr.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 736 B |
@@ -140,7 +140,7 @@ expand_content_files= \
|
||||
# e.g. GTKDOC_CFLAGS=-I$(top_srcdir) -I$(top_builddir) $(GTK_DEBUG_FLAGS)
|
||||
# e.g. GTKDOC_LIBS=$(top_builddir)/gtk/$(gtktargetlib)
|
||||
GTKDOC_CFLAGS=$(MUTTER_CFLAGS)
|
||||
GTKDOC_LIBS=$(MUTTER_LIBS) $(top_builddir)/src/libmutter.la
|
||||
GTKDOC_LIBS=$(MUTTER_LIBS) $(top_builddir)/src/libmutter-wayland.la
|
||||
|
||||
# This includes the standard gtk-doc make rules, copied by gtkdocize.
|
||||
include $(top_srcdir)/gtk-doc.make
|
||||
|
||||
@@ -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
|
||||
|
||||
1
protocol/Makefile.am
Normal file
1
protocol/Makefile.am
Normal file
@@ -0,0 +1 @@
|
||||
EXTRA_DIST = xserver.xml
|
||||
30
protocol/gtk-shell.xml
Normal file
30
protocol/gtk-shell.xml
Normal 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>
|
||||
18
protocol/xserver.xml
Normal file
18
protocol/xserver.xml
Normal file
@@ -0,0 +1,18 @@
|
||||
<protocol name="xserver">
|
||||
|
||||
<interface name="xserver" version="1">
|
||||
<request name="set_window_id">
|
||||
<arg name="surface" type="object" interface="wl_surface"/>
|
||||
<arg name="id" type="uint"/>
|
||||
</request>
|
||||
|
||||
<event name="client">
|
||||
<arg name="fd" type="fd"/>
|
||||
</event>
|
||||
|
||||
<event name="listen_socket">
|
||||
<arg name="fd" type="fd"/>
|
||||
</event>
|
||||
</interface>
|
||||
|
||||
</protocol>
|
||||
135
src/Makefile.am
135
src/Makefile.am
@@ -1,7 +1,7 @@
|
||||
# Flag build for parallelism; see https://savannah.gnu.org/patch/?6905
|
||||
.AUTOPARALLEL:
|
||||
|
||||
lib_LTLIBRARIES = libmutter.la
|
||||
lib_LTLIBRARIES = libmutter-wayland.la
|
||||
|
||||
SUBDIRS=compositor/plugins
|
||||
|
||||
@@ -10,6 +10,7 @@ INCLUDES= \
|
||||
-DCOGL_ENABLE_EXPERIMENTAL_API \
|
||||
-DCOGL_ENABLE_EXPERIMENTAL_2_0_API \
|
||||
$(MUTTER_CFLAGS) \
|
||||
-I$(top_builddir) \
|
||||
-I$(srcdir) \
|
||||
-I$(srcdir)/core \
|
||||
-I$(srcdir)/ui \
|
||||
@@ -29,12 +30,24 @@ INCLUDES= \
|
||||
-DMUTTER_PLUGIN_DIR=\"@MUTTER_PLUGIN_DIR@\" \
|
||||
-DGETTEXT_PACKAGE=\"$(GETTEXT_PACKAGE)\"
|
||||
|
||||
mutter_built_sources = \
|
||||
$(dbus_xrandr_built_sources) \
|
||||
mutter-enum-types.h \
|
||||
mutter-enum-types.c
|
||||
INCLUDES += \
|
||||
-I$(srcdir)/wayland \
|
||||
-I$(builddir)/wayland \
|
||||
-DXWAYLAND_PATH='"@XWAYLAND_PATH@"'
|
||||
|
||||
libmutter_la_SOURCES = \
|
||||
mutter_built_sources = \
|
||||
$(dbus_idle_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
|
||||
|
||||
libmutter_wayland_la_SOURCES = \
|
||||
core/async-getprop.c \
|
||||
core/async-getprop.h \
|
||||
core/barrier.c \
|
||||
@@ -63,6 +76,7 @@ libmutter_la_SOURCES = \
|
||||
compositor/meta-shadow-factory.c \
|
||||
compositor/meta-shadow-factory-private.h \
|
||||
compositor/meta-shaped-texture.c \
|
||||
compositor/meta-shaped-texture-private.h \
|
||||
compositor/meta-texture-rectangle.c \
|
||||
compositor/meta-texture-rectangle.h \
|
||||
compositor/meta-texture-tower.c \
|
||||
@@ -113,6 +127,10 @@ libmutter_la_SOURCES = \
|
||||
core/keybindings.c \
|
||||
core/keybindings-private.h \
|
||||
core/main.c \
|
||||
core/meta-cursor-tracker.c \
|
||||
core/meta-cursor-tracker-private.h \
|
||||
core/meta-idle-monitor.c \
|
||||
core/meta-idle-monitor-private.h \
|
||||
core/meta-xrandr-shared.h \
|
||||
core/monitor.c \
|
||||
core/monitor-config.c \
|
||||
@@ -166,8 +184,29 @@ libmutter_la_SOURCES = \
|
||||
ui/ui.c \
|
||||
$(mutter_built_sources)
|
||||
|
||||
libmutter_la_LDFLAGS = -no-undefined
|
||||
libmutter_la_LIBADD = $(MUTTER_LIBS)
|
||||
libmutter_wayland_la_SOURCES += \
|
||||
wayland/meta-wayland.c \
|
||||
wayland/meta-wayland-private.h \
|
||||
wayland/meta-xwayland-private.h \
|
||||
wayland/meta-xwayland.c \
|
||||
wayland/meta-wayland-data-device.c \
|
||||
wayland/meta-wayland-data-device.h \
|
||||
wayland/meta-wayland-keyboard.c \
|
||||
wayland/meta-wayland-keyboard.h \
|
||||
wayland/meta-wayland-pointer.c \
|
||||
wayland/meta-wayland-pointer.h \
|
||||
wayland/meta-wayland-seat.c \
|
||||
wayland/meta-wayland-seat.h \
|
||||
wayland/meta-wayland-stage.h \
|
||||
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)
|
||||
|
||||
# Headers installed for plugins; introspected information will
|
||||
# be extracted into Mutter-<version>.gir
|
||||
@@ -186,6 +225,8 @@ libmutterinclude_base_headers = \
|
||||
meta/meta-background-actor.h \
|
||||
meta/meta-background-group.h \
|
||||
meta/meta-background.h \
|
||||
meta/meta-cursor-tracker.h \
|
||||
meta/meta-idle-monitor.h \
|
||||
meta/meta-plugin.h \
|
||||
meta/meta-shaped-texture.h \
|
||||
meta/meta-shadow-factory.h \
|
||||
@@ -203,16 +244,27 @@ libmutterinclude_base_headers = \
|
||||
libmutterinclude_extra_headers = \
|
||||
meta/atomnames.h
|
||||
|
||||
libmutterincludedir = $(includedir)/mutter/meta
|
||||
libmutterincludedir = $(includedir)/mutter-wayland/meta
|
||||
|
||||
libmutterinclude_HEADERS = \
|
||||
$(libmutterinclude_base_headers) \
|
||||
$(libmutterinclude_extra_headers)
|
||||
|
||||
bin_PROGRAMS=mutter
|
||||
bin_PROGRAMS=mutter-wayland
|
||||
|
||||
mutter_SOURCES = core/mutter.c
|
||||
mutter_LDADD = $(MUTTER_LIBS) libmutter.la
|
||||
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)
|
||||
@@ -234,15 +286,15 @@ typelib_DATA = Meta-$(api_version).typelib
|
||||
|
||||
INTROSPECTION_GIRS = Meta-$(api_version).gir
|
||||
|
||||
Meta-$(api_version).gir: libmutter.la
|
||||
Meta-$(api_version).gir: libmutter-wayland.la
|
||||
@META_GIR@_INCLUDES = GObject-2.0 GDesktopEnums-3.0 Gdk-3.0 Gtk-3.0 Clutter-1.0 xlib-2.0 xfixes-4.0 Cogl-1.0
|
||||
@META_GIR@_EXPORT_PACKAGES = libmutter
|
||||
@META_GIR@_EXPORT_PACKAGES = libmutter-wayland
|
||||
@META_GIR@_CFLAGS = $(INCLUDES)
|
||||
@META_GIR@_LIBS = libmutter.la
|
||||
@META_GIR@_LIBS = libmutter-wayland.la
|
||||
@META_GIR@_FILES = \
|
||||
mutter-enum-types.h \
|
||||
$(libmutterinclude_base_headers) \
|
||||
$(filter %.c,$(libmutter_la_SOURCES))
|
||||
$(filter %.c,$(libmutter_wayland_la_SOURCES))
|
||||
@META_GIR@_SCANNERFLAGS = --warn-all --warn-error
|
||||
|
||||
endif
|
||||
@@ -253,22 +305,17 @@ testasyncgetprop_SOURCES = core/testasyncgetprop.c
|
||||
|
||||
noinst_PROGRAMS=testboxes testgradient testasyncgetprop
|
||||
|
||||
testboxes_LDADD = $(MUTTER_LIBS) libmutter.la
|
||||
testgradient_LDADD = $(MUTTER_LIBS) libmutter.la
|
||||
testasyncgetprop_LDADD = $(MUTTER_LIBS) libmutter.la
|
||||
testboxes_LDADD = $(MUTTER_LIBS) libmutter-wayland.la
|
||||
testgradient_LDADD = $(MUTTER_LIBS) libmutter-wayland.la
|
||||
testasyncgetprop_LDADD = $(MUTTER_LIBS) libmutter-wayland.la
|
||||
|
||||
@INTLTOOL_DESKTOP_RULE@
|
||||
|
||||
desktopfilesdir=$(datadir)/applications
|
||||
desktopfiles_in_files=mutter.desktop.in
|
||||
desktopfiles_in_files=mutter-wayland.desktop.in
|
||||
desktopfiles_files=$(desktopfiles_in_files:.desktop.in=.desktop)
|
||||
desktopfiles_DATA = $(desktopfiles_files)
|
||||
|
||||
wmpropertiesdir=$(datadir)/gnome/wm-properties
|
||||
wmproperties_in_files=mutter-wm.desktop.in
|
||||
wmproperties_files=$(wmproperties_in_files:.desktop.in=.desktop)
|
||||
wmproperties_DATA = $(wmproperties_files)
|
||||
|
||||
xmldir = @GNOME_KEYBINDINGS_KEYSDIR@
|
||||
xml_in_files = \
|
||||
50-mutter-navigation.xml.in \
|
||||
@@ -276,7 +323,9 @@ xml_in_files = \
|
||||
50-mutter-windows.xml.in
|
||||
xml_DATA = $(xml_in_files:.xml.in=.xml)
|
||||
|
||||
gsettings_SCHEMAS = org.gnome.mutter.gschema.xml
|
||||
dbus_idle_built_sources = meta-dbus-idle-monitor.c meta-dbus-idle-monitor.h
|
||||
|
||||
gsettings_SCHEMAS = org.gnome.mutter.gschema.xml org.gnome.mutter.wayland.gschema.xml
|
||||
@INTLTOOL_XML_NOMERGE_RULE@
|
||||
@GSETTINGS_RULES@
|
||||
|
||||
@@ -284,9 +333,10 @@ convertdir = $(datadir)/GConf/gsettings
|
||||
convert_DATA = mutter-schemas.convert
|
||||
|
||||
CLEANFILES = \
|
||||
mutter.desktop \
|
||||
mutter-wayland.desktop \
|
||||
mutter-wm.desktop \
|
||||
org.gnome.mutter.gschema.xml \
|
||||
org.gnome.mutter.wayland.gschema.xml \
|
||||
$(xml_DATA) \
|
||||
$(mutter_built_sources) \
|
||||
$(typelib_DATA) \
|
||||
@@ -294,7 +344,7 @@ CLEANFILES = \
|
||||
|
||||
pkgconfigdir = $(libdir)/pkgconfig
|
||||
|
||||
pkgconfig_DATA = libmutter.pc mutter-plugins.pc
|
||||
pkgconfig_DATA = libmutter-wayland.pc
|
||||
|
||||
EXTRA_DIST=$(desktopfiles_files) \
|
||||
$(wmproperties_files) \
|
||||
@@ -303,11 +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.pc.in \
|
||||
mutter-plugins.pc.in \
|
||||
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
|
||||
@@ -335,8 +386,28 @@ mutter-enum-types.c: stamp-mutter-enum-types.h mutter-enum-types.c.in
|
||||
dbus_xrandr_built_sources = meta-dbus-xrandr.c meta-dbus-xrandr.h
|
||||
|
||||
$(dbus_xrandr_built_sources) : Makefile.am xrandr.xml
|
||||
$(AM_V_GEN)gdbus-codegen \
|
||||
$(AM_V_GEN)gdbus-codegen \
|
||||
--interface-prefix org.gnome.Mutter \
|
||||
--c-namespace MetaDBus \
|
||||
--generate-c-code meta-dbus-xrandr \
|
||||
$(srcdir)/xrandr.xml
|
||||
|
||||
dbus_idle_built_sources = meta-dbus-idle-monitor.c meta-dbus-idle-monitor.h
|
||||
|
||||
$(dbus_idle_built_sources) : Makefile.am idle-monitor.xml
|
||||
$(AM_V_GEN)gdbus-codegen \
|
||||
--interface-prefix org.gnome.Mutter \
|
||||
--c-namespace MetaDBus \
|
||||
--generate-c-code meta-dbus-idle-monitor \
|
||||
--c-generate-object-manager \
|
||||
$(srcdir)/idle-monitor.xml
|
||||
|
||||
wayland/%-protocol.c : $(top_builddir)/protocol/%.xml
|
||||
mkdir -p wayland
|
||||
$(AM_V_GEN)$(WAYLAND_SCANNER) code < $< > $@
|
||||
wayland/%-server-protocol.h : $(top_builddir)/protocol/%.xml
|
||||
mkdir -p wayland
|
||||
$(AM_V_GEN)$(WAYLAND_SCANNER) server-header < $< > $@
|
||||
wayland/%-client-protocol.h : $(top_builddir)/protocol/%.xml
|
||||
mkdir -p wayland
|
||||
$(AM_V_GEN)$(WAYLAND_SCANNER) client-header < $< > $@
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -66,8 +66,6 @@ void meta_switch_workspace_completed (MetaScreen *screen);
|
||||
|
||||
gboolean meta_begin_modal_for_plugin (MetaScreen *screen,
|
||||
MetaPlugin *plugin,
|
||||
Window grab_window,
|
||||
Cursor cursor,
|
||||
MetaModalOptions options,
|
||||
guint32 timestamp);
|
||||
void meta_end_modal_for_plugin (MetaScreen *screen,
|
||||
|
||||
@@ -84,6 +84,9 @@
|
||||
#include "meta-window-group.h"
|
||||
#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>
|
||||
|
||||
@@ -172,7 +175,7 @@ process_damage (MetaCompositor *compositor,
|
||||
if (window_actor == NULL)
|
||||
return;
|
||||
|
||||
meta_window_actor_process_damage (window_actor, event);
|
||||
meta_window_actor_process_x11_damage (window_actor, event);
|
||||
}
|
||||
|
||||
static void
|
||||
@@ -327,29 +330,37 @@ void
|
||||
meta_set_stage_input_region (MetaScreen *screen,
|
||||
XserverRegion region)
|
||||
{
|
||||
MetaCompScreen *info = meta_screen_get_compositor_data (screen);
|
||||
MetaDisplay *display = meta_screen_get_display (screen);
|
||||
Display *xdpy = meta_display_get_xdisplay (display);
|
||||
/* As a wayland compositor we can simply ignore all this trickery
|
||||
* for setting an input region on the stage for capturing events in
|
||||
* clutter since all input comes to us first and we get to choose
|
||||
* who else sees them.
|
||||
*/
|
||||
if (!meta_is_wayland_compositor ())
|
||||
{
|
||||
MetaCompScreen *info = meta_screen_get_compositor_data (screen);
|
||||
MetaDisplay *display = meta_screen_get_display (screen);
|
||||
Display *xdpy = meta_display_get_xdisplay (display);
|
||||
|
||||
if (info->stage && info->output)
|
||||
{
|
||||
do_set_stage_input_region (screen, region);
|
||||
if (info->stage && info->output)
|
||||
{
|
||||
do_set_stage_input_region (screen, region);
|
||||
}
|
||||
else
|
||||
{
|
||||
/* Reset info->pending_input_region if one existed before and set the new
|
||||
* one to use it later. */
|
||||
if (info->pending_input_region)
|
||||
{
|
||||
XFixesDestroyRegion (xdpy, info->pending_input_region);
|
||||
info->pending_input_region = None;
|
||||
}
|
||||
if (region != None)
|
||||
{
|
||||
info->pending_input_region = XFixesCreateRegion (xdpy, NULL, 0);
|
||||
XFixesCopyRegion (xdpy, info->pending_input_region, region);
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
/* Reset info->pending_input_region if one existed before and set the new
|
||||
* one to use it later. */
|
||||
if (info->pending_input_region)
|
||||
{
|
||||
XFixesDestroyRegion (xdpy, info->pending_input_region);
|
||||
info->pending_input_region = None;
|
||||
}
|
||||
if (region != None)
|
||||
{
|
||||
info->pending_input_region = XFixesCreateRegion (xdpy, NULL, 0);
|
||||
XFixesCopyRegion (xdpy, info->pending_input_region, region);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
@@ -380,56 +391,55 @@ meta_focus_stage_window (MetaScreen *screen,
|
||||
if (!stage)
|
||||
return;
|
||||
|
||||
window = clutter_x11_get_stage_window (stage);
|
||||
if (!meta_is_wayland_compositor ())
|
||||
{
|
||||
window = clutter_x11_get_stage_window (stage);
|
||||
|
||||
if (window == None)
|
||||
return;
|
||||
if (window == None)
|
||||
return;
|
||||
|
||||
meta_display_set_input_focus_xwindow (screen->display,
|
||||
screen,
|
||||
window,
|
||||
timestamp);
|
||||
meta_display_set_input_focus_xwindow (screen->display,
|
||||
screen,
|
||||
META_FOCUS_STAGE,
|
||||
window,
|
||||
timestamp);
|
||||
}
|
||||
else
|
||||
{
|
||||
meta_display_set_input_focus_xwindow (screen->display,
|
||||
screen,
|
||||
META_FOCUS_STAGE,
|
||||
None,
|
||||
timestamp);
|
||||
}
|
||||
}
|
||||
|
||||
gboolean
|
||||
meta_stage_is_focused (MetaScreen *screen)
|
||||
{
|
||||
ClutterStage *stage;
|
||||
Window window;
|
||||
|
||||
stage = CLUTTER_STAGE (meta_get_stage_for_screen (screen));
|
||||
if (!stage)
|
||||
return FALSE;
|
||||
|
||||
window = clutter_x11_get_stage_window (stage);
|
||||
|
||||
if (window == None)
|
||||
return FALSE;
|
||||
|
||||
return (screen->display->focus_xwindow == window);
|
||||
return (screen->display->focus_type == META_FOCUS_STAGE);
|
||||
}
|
||||
|
||||
gboolean
|
||||
meta_begin_modal_for_plugin (MetaScreen *screen,
|
||||
MetaPlugin *plugin,
|
||||
Window grab_window,
|
||||
Cursor cursor,
|
||||
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;
|
||||
gboolean pointer_grabbed = FALSE;
|
||||
gboolean keyboard_grabbed = FALSE;
|
||||
int result;
|
||||
|
||||
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)
|
||||
{
|
||||
@@ -479,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:
|
||||
@@ -498,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,
|
||||
@@ -509,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;
|
||||
@@ -562,6 +649,11 @@ redirect_windows (MetaCompositor *compositor,
|
||||
guint n_retries;
|
||||
guint max_retries;
|
||||
|
||||
/* If we're running with wayland, connected to a headless xwayland
|
||||
* server then all the windows are implicitly redirected offscreen
|
||||
* already and it would generate an error to try and explicitly
|
||||
* redirect them via XCompositeRedirectSubwindows() */
|
||||
|
||||
if (meta_get_replace_current_wm ())
|
||||
max_retries = 5;
|
||||
else
|
||||
@@ -602,8 +694,9 @@ 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;
|
||||
|
||||
/* Check if the screen is already managed */
|
||||
if (meta_screen_get_compositor_data (screen))
|
||||
@@ -616,7 +709,14 @@ meta_compositor_manage_screen (MetaCompositor *compositor,
|
||||
* We have to initialize info->pending_input_region to an empty region explicitly,
|
||||
* because None value is used to mean that the whole screen is an input region.
|
||||
*/
|
||||
info->pending_input_region = XFixesCreateRegion (xdisplay, NULL, 0);
|
||||
if (!meta_is_wayland_compositor ())
|
||||
info->pending_input_region = XFixesCreateRegion (xdisplay, NULL, 0);
|
||||
else
|
||||
{
|
||||
/* Stage input region trickery isn't needed when we're running as a
|
||||
* wayland compositor. */
|
||||
info->pending_input_region = None;
|
||||
}
|
||||
|
||||
info->screen = screen;
|
||||
|
||||
@@ -627,7 +727,56 @@ meta_compositor_manage_screen (MetaCompositor *compositor,
|
||||
|
||||
meta_screen_set_cm_selection (screen);
|
||||
|
||||
info->stage = clutter_stage_new ();
|
||||
/* We will have already created a stage if running as a wayland
|
||||
* compositor... */
|
||||
if (meta_is_wayland_compositor ())
|
||||
{
|
||||
wayland_compositor = meta_wayland_compositor_get_default ();
|
||||
info->stage = wayland_compositor->stage;
|
||||
|
||||
meta_screen_get_size (screen, &width, &height);
|
||||
clutter_actor_set_size (info->stage, width, height);
|
||||
}
|
||||
else
|
||||
{
|
||||
info->stage = clutter_stage_new ();
|
||||
|
||||
meta_screen_get_size (screen, &width, &height);
|
||||
clutter_actor_realize (info->stage);
|
||||
|
||||
xwin = clutter_x11_get_stage_window (CLUTTER_STAGE (info->stage));
|
||||
|
||||
XResizeWindow (xdisplay, xwin, width, height);
|
||||
|
||||
{
|
||||
long event_mask;
|
||||
unsigned char mask_bits[XIMaskLen (XI_LASTEVENT)] = { 0 };
|
||||
XIEventMask mask = { XIAllMasterDevices, sizeof (mask_bits), mask_bits };
|
||||
XWindowAttributes attr;
|
||||
|
||||
meta_core_add_old_event_mask (xdisplay, xwin, &mask);
|
||||
|
||||
XISetMask (mask.mask, XI_KeyPress);
|
||||
XISetMask (mask.mask, XI_KeyRelease);
|
||||
XISetMask (mask.mask, XI_ButtonPress);
|
||||
XISetMask (mask.mask, XI_ButtonRelease);
|
||||
XISetMask (mask.mask, XI_Enter);
|
||||
XISetMask (mask.mask, XI_Leave);
|
||||
XISetMask (mask.mask, XI_FocusIn);
|
||||
XISetMask (mask.mask, XI_FocusOut);
|
||||
XISetMask (mask.mask, XI_Motion);
|
||||
XIClearMask (mask.mask, XI_TouchBegin);
|
||||
XIClearMask (mask.mask, XI_TouchEnd);
|
||||
XIClearMask (mask.mask, XI_TouchUpdate);
|
||||
XISelectEvents (xdisplay, xwin, &mask, 1);
|
||||
|
||||
event_mask = ExposureMask | PropertyChangeMask | StructureNotifyMask;
|
||||
if (XGetWindowAttributes (xdisplay, xwin, &attr))
|
||||
event_mask |= attr.your_event_mask;
|
||||
|
||||
XSelectInput (xdisplay, xwin, event_mask);
|
||||
}
|
||||
}
|
||||
|
||||
clutter_stage_set_paint_callback (CLUTTER_STAGE (info->stage),
|
||||
after_stage_paint,
|
||||
@@ -636,42 +785,6 @@ meta_compositor_manage_screen (MetaCompositor *compositor,
|
||||
|
||||
clutter_stage_set_sync_delay (CLUTTER_STAGE (info->stage), META_SYNC_DELAY);
|
||||
|
||||
meta_screen_get_size (screen, &width, &height);
|
||||
clutter_actor_realize (info->stage);
|
||||
|
||||
xwin = clutter_x11_get_stage_window (CLUTTER_STAGE (info->stage));
|
||||
|
||||
XResizeWindow (xdisplay, xwin, width, height);
|
||||
|
||||
{
|
||||
long event_mask;
|
||||
unsigned char mask_bits[XIMaskLen (XI_LASTEVENT)] = { 0 };
|
||||
XIEventMask mask = { XIAllMasterDevices, sizeof (mask_bits), mask_bits };
|
||||
XWindowAttributes attr;
|
||||
|
||||
meta_core_add_old_event_mask (xdisplay, xwin, &mask);
|
||||
|
||||
XISetMask (mask.mask, XI_KeyPress);
|
||||
XISetMask (mask.mask, XI_KeyRelease);
|
||||
XISetMask (mask.mask, XI_ButtonPress);
|
||||
XISetMask (mask.mask, XI_ButtonRelease);
|
||||
XISetMask (mask.mask, XI_Enter);
|
||||
XISetMask (mask.mask, XI_Leave);
|
||||
XISetMask (mask.mask, XI_FocusIn);
|
||||
XISetMask (mask.mask, XI_FocusOut);
|
||||
XISetMask (mask.mask, XI_Motion);
|
||||
XIClearMask (mask.mask, XI_TouchBegin);
|
||||
XIClearMask (mask.mask, XI_TouchEnd);
|
||||
XIClearMask (mask.mask, XI_TouchUpdate);
|
||||
XISelectEvents (xdisplay, xwin, &mask, 1);
|
||||
|
||||
event_mask = ExposureMask | PropertyChangeMask | StructureNotifyMask;
|
||||
if (XGetWindowAttributes (xdisplay, xwin, &attr))
|
||||
event_mask |= attr.your_event_mask;
|
||||
|
||||
XSelectInput (xdisplay, xwin, event_mask);
|
||||
}
|
||||
|
||||
info->window_group = meta_window_group_new (screen);
|
||||
info->top_window_group = meta_window_group_new (screen);
|
||||
|
||||
@@ -680,53 +793,66 @@ meta_compositor_manage_screen (MetaCompositor *compositor,
|
||||
|
||||
info->plugin_mgr = meta_plugin_manager_new (screen);
|
||||
|
||||
/*
|
||||
* Delay the creation of the overlay window as long as we can, to avoid
|
||||
* blanking out the screen. This means that during the plugin loading, the
|
||||
* overlay window is not accessible; if the plugin needs to access it
|
||||
* directly, it should hook into the "show" signal on stage, and do
|
||||
* its stuff there.
|
||||
*/
|
||||
info->output = get_output_window (screen);
|
||||
XReparentWindow (xdisplay, xwin, info->output, 0, 0);
|
||||
|
||||
/* Make sure there isn't any left-over output shape on the
|
||||
* overlay window by setting the whole screen to be an
|
||||
* output region.
|
||||
*
|
||||
* Note: there doesn't seem to be any real chance of that
|
||||
* because the X server will destroy the overlay window
|
||||
* when the last client using it exits.
|
||||
*/
|
||||
XFixesSetWindowShapeRegion (xdisplay, info->output, ShapeBounding, 0, 0, None);
|
||||
|
||||
do_set_stage_input_region (screen, info->pending_input_region);
|
||||
if (info->pending_input_region != None)
|
||||
if (meta_is_wayland_compositor ())
|
||||
{
|
||||
XFixesDestroyRegion (xdisplay, info->pending_input_region);
|
||||
info->pending_input_region = None;
|
||||
/* NB: When running as a wayland compositor we don't need an X
|
||||
* composite overlay window, and we don't need to play any input
|
||||
* region tricks to redirect events into clutter. */
|
||||
info->output = None;
|
||||
}
|
||||
else
|
||||
{
|
||||
/*
|
||||
* Delay the creation of the overlay window as long as we can, to avoid
|
||||
* blanking out the screen. This means that during the plugin loading, the
|
||||
* overlay window is not accessible; if the plugin needs to access it
|
||||
* directly, it should hook into the "show" signal on stage, and do
|
||||
* its stuff there.
|
||||
*/
|
||||
info->output = get_output_window (screen);
|
||||
XReparentWindow (xdisplay, xwin, info->output, 0, 0);
|
||||
|
||||
/* Map overlay window before redirecting windows offscreen so we catch their
|
||||
* contents until we show the stage.
|
||||
*/
|
||||
XMapWindow (xdisplay, info->output);
|
||||
/* Make sure there isn't any left-over output shape on the
|
||||
* overlay window by setting the whole screen to be an
|
||||
* output region.
|
||||
*
|
||||
* Note: there doesn't seem to be any real chance of that
|
||||
* because the X server will destroy the overlay window
|
||||
* when the last client using it exits.
|
||||
*/
|
||||
XFixesSetWindowShapeRegion (xdisplay, info->output, ShapeBounding, 0, 0, None);
|
||||
|
||||
redirect_windows (compositor, screen);
|
||||
do_set_stage_input_region (screen, info->pending_input_region);
|
||||
if (info->pending_input_region != None)
|
||||
{
|
||||
XFixesDestroyRegion (xdisplay, info->pending_input_region);
|
||||
info->pending_input_region = None;
|
||||
}
|
||||
|
||||
/* Map overlay window before redirecting windows offscreen so we catch their
|
||||
* contents until we show the stage.
|
||||
*/
|
||||
XMapWindow (xdisplay, info->output);
|
||||
|
||||
redirect_windows (compositor, screen);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
meta_compositor_unmanage_screen (MetaCompositor *compositor,
|
||||
MetaScreen *screen)
|
||||
{
|
||||
MetaDisplay *display = meta_screen_get_display (screen);
|
||||
Display *xdisplay = meta_display_get_xdisplay (display);
|
||||
Window xroot = meta_screen_get_xroot (screen);
|
||||
if (!meta_is_wayland_compositor ())
|
||||
{
|
||||
MetaDisplay *display = meta_screen_get_display (screen);
|
||||
Display *xdisplay = meta_display_get_xdisplay (display);
|
||||
Window xroot = meta_screen_get_xroot (screen);
|
||||
|
||||
/* This is the most important part of cleanup - we have to do this
|
||||
* before giving up the window manager selection or the next
|
||||
* window manager won't be able to redirect subwindows */
|
||||
XCompositeUnredirectSubwindows (xdisplay, xroot, CompositeRedirectManual);
|
||||
/* This is the most important part of cleanup - we have to do this
|
||||
* before giving up the window manager selection or the next
|
||||
* window manager won't be able to redirect subwindows */
|
||||
XCompositeUnredirectSubwindows (xdisplay, xroot, CompositeRedirectManual);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -798,15 +924,18 @@ meta_compositor_remove_window (MetaCompositor *compositor,
|
||||
if (!window_actor)
|
||||
return;
|
||||
|
||||
screen = meta_window_get_screen (window);
|
||||
info = meta_screen_get_compositor_data (screen);
|
||||
|
||||
if (window_actor == info->unredirected_window)
|
||||
if (!meta_is_wayland_compositor ())
|
||||
{
|
||||
meta_window_actor_set_redirected (window_actor, TRUE);
|
||||
meta_shape_cow_for_window (meta_window_get_screen (meta_window_actor_get_meta_window (info->unredirected_window)),
|
||||
NULL);
|
||||
info->unredirected_window = NULL;
|
||||
screen = meta_window_get_screen (window);
|
||||
info = meta_screen_get_compositor_data (screen);
|
||||
|
||||
if (window_actor == info->unredirected_window)
|
||||
{
|
||||
meta_window_actor_set_redirected (window_actor, TRUE);
|
||||
meta_shape_cow_for_window (meta_window_get_screen (meta_window_actor_get_meta_window (info->unredirected_window)),
|
||||
NULL);
|
||||
info->unredirected_window = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
meta_window_actor_destroy (window_actor);
|
||||
@@ -993,7 +1122,8 @@ meta_compositor_process_event (MetaCompositor *compositor,
|
||||
break;
|
||||
|
||||
default:
|
||||
if (event->type == meta_display_get_damage_event_base (compositor->display) + XDamageNotify)
|
||||
if (!meta_is_wayland_compositor () &&
|
||||
event->type == meta_display_get_damage_event_base (compositor->display) + XDamageNotify)
|
||||
{
|
||||
/* Core code doesn't handle damage events, so we need to extract the MetaWindow
|
||||
* ourselves
|
||||
@@ -1012,7 +1142,7 @@ meta_compositor_process_event (MetaCompositor *compositor,
|
||||
|
||||
/* Clutter needs to know about MapNotify events otherwise it will
|
||||
think the stage is invisible */
|
||||
if (event->type == MapNotify)
|
||||
if (!meta_is_wayland_compositor () && event->type == MapNotify)
|
||||
clutter_x11_handle_event (event);
|
||||
|
||||
/* The above handling is basically just "observing" the events, so we return
|
||||
@@ -1145,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
|
||||
@@ -1356,20 +1487,38 @@ meta_compositor_sync_screen_size (MetaCompositor *compositor,
|
||||
{
|
||||
MetaDisplay *display = meta_screen_get_display (screen);
|
||||
MetaCompScreen *info = meta_screen_get_compositor_data (screen);
|
||||
Display *xdisplay;
|
||||
Window xwin;
|
||||
|
||||
DEBUG_TRACE ("meta_compositor_sync_screen_size\n");
|
||||
g_return_if_fail (info);
|
||||
if (meta_is_wayland_compositor ())
|
||||
{
|
||||
/* FIXME: when we support a sliced stage, this is the place to do it
|
||||
But! This is not the place to apply KMS config, here we only
|
||||
notify Clutter/Cogl/GL that the framebuffer sizes changed.
|
||||
|
||||
xdisplay = meta_display_get_xdisplay (display);
|
||||
xwin = clutter_x11_get_stage_window (CLUTTER_STAGE (info->stage));
|
||||
And because for now clutter does not do sliced, we use one
|
||||
framebuffer the size of the whole screen, and when running on
|
||||
bare metal MetaMonitorManager will do the necessary tricks to
|
||||
show the right portions on the right screens.
|
||||
*/
|
||||
|
||||
XResizeWindow (xdisplay, xwin, width, height);
|
||||
clutter_actor_set_size (info->stage, width, height);
|
||||
}
|
||||
else
|
||||
{
|
||||
Display *xdisplay;
|
||||
Window xwin;
|
||||
|
||||
DEBUG_TRACE ("meta_compositor_sync_screen_size\n");
|
||||
g_return_if_fail (info);
|
||||
|
||||
xdisplay = meta_display_get_xdisplay (display);
|
||||
xwin = clutter_x11_get_stage_window (CLUTTER_STAGE (info->stage));
|
||||
|
||||
XResizeWindow (xdisplay, xwin, width, height);
|
||||
}
|
||||
|
||||
meta_verbose ("Changed size for stage on screen %d to %dx%d\n",
|
||||
meta_screen_get_screen_number (screen),
|
||||
width, height);
|
||||
meta_screen_get_screen_number (screen),
|
||||
width, height);
|
||||
}
|
||||
|
||||
static void
|
||||
@@ -1433,29 +1582,32 @@ pre_paint_windows (MetaCompScreen *info)
|
||||
if (info->windows == NULL)
|
||||
return;
|
||||
|
||||
top_window = g_list_last (info->windows)->data;
|
||||
|
||||
if (meta_window_actor_should_unredirect (top_window) &&
|
||||
info->disable_unredirect_count == 0)
|
||||
expected_unredirected_window = top_window;
|
||||
|
||||
if (info->unredirected_window != expected_unredirected_window)
|
||||
if (!meta_is_wayland_compositor ())
|
||||
{
|
||||
if (info->unredirected_window != NULL)
|
||||
{
|
||||
meta_window_actor_set_redirected (info->unredirected_window, TRUE);
|
||||
meta_shape_cow_for_window (meta_window_get_screen (meta_window_actor_get_meta_window (info->unredirected_window)),
|
||||
NULL);
|
||||
}
|
||||
top_window = g_list_last (info->windows)->data;
|
||||
|
||||
if (expected_unredirected_window != NULL)
|
||||
{
|
||||
meta_shape_cow_for_window (meta_window_get_screen (meta_window_actor_get_meta_window (top_window)),
|
||||
meta_window_actor_get_meta_window (top_window));
|
||||
meta_window_actor_set_redirected (top_window, FALSE);
|
||||
}
|
||||
if (meta_window_actor_should_unredirect (top_window) &&
|
||||
info->disable_unredirect_count == 0)
|
||||
expected_unredirected_window = top_window;
|
||||
|
||||
info->unredirected_window = expected_unredirected_window;
|
||||
if (info->unredirected_window != expected_unredirected_window)
|
||||
{
|
||||
if (info->unredirected_window != NULL)
|
||||
{
|
||||
meta_window_actor_set_redirected (info->unredirected_window, TRUE);
|
||||
meta_shape_cow_for_window (meta_window_get_screen (meta_window_actor_get_meta_window (info->unredirected_window)),
|
||||
NULL);
|
||||
}
|
||||
|
||||
if (expected_unredirected_window != NULL)
|
||||
{
|
||||
meta_shape_cow_for_window (meta_window_get_screen (meta_window_actor_get_meta_window (top_window)),
|
||||
meta_window_actor_get_meta_window (top_window));
|
||||
meta_window_actor_set_redirected (top_window, FALSE);
|
||||
}
|
||||
|
||||
info->unredirected_window = expected_unredirected_window;
|
||||
}
|
||||
}
|
||||
|
||||
for (l = info->windows; l; l = l->next)
|
||||
|
||||
@@ -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 */
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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 */
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1031,7 +1031,6 @@ meta_background_load_file_finish (MetaBackground *self,
|
||||
GAsyncResult *result,
|
||||
GError **error)
|
||||
{
|
||||
static CoglUserDataKey key;
|
||||
GTask *task;
|
||||
LoadFileTaskData *task_data;
|
||||
CoglTexture *texture;
|
||||
@@ -1077,12 +1076,6 @@ meta_background_load_file_finish (MetaBackground *self,
|
||||
goto out;
|
||||
}
|
||||
|
||||
cogl_object_set_user_data (COGL_OBJECT (texture),
|
||||
&key,
|
||||
g_object_ref (pixbuf),
|
||||
(CoglUserDataDestroyCallback)
|
||||
g_object_unref);
|
||||
|
||||
ensure_pipeline (self);
|
||||
unset_texture (self);
|
||||
set_style (self, task_data->style);
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -329,8 +329,18 @@ meta_plugin_manager_xevent_filter (MetaPluginManager *plugin_mgr,
|
||||
*/
|
||||
if (klass->xevent_filter)
|
||||
return klass->xevent_filter (plugin, xev);
|
||||
else
|
||||
return clutter_x11_handle_event (xev) != CLUTTER_X11_FILTER_CONTINUE;
|
||||
|
||||
/* When mutter is running as a wayland compositor, things like input
|
||||
* events just come directly from clutter so it won't have disabled
|
||||
* clutter's event retrieval and won't need to forward it events (if
|
||||
* it did it would lead to recursion). Also when running as a
|
||||
* wayland compositor we shouldn't be assuming that we're running
|
||||
* with the clutter x11 backend.
|
||||
*/
|
||||
if (meta_is_wayland_compositor ())
|
||||
return FALSE;
|
||||
|
||||
return clutter_x11_handle_event (xev) != CLUTTER_X11_FILTER_CONTINUE;
|
||||
}
|
||||
|
||||
void
|
||||
|
||||
@@ -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
|
||||
@@ -267,10 +265,6 @@ meta_plugin_destroy_completed (MetaPlugin *plugin,
|
||||
/**
|
||||
* meta_plugin_begin_modal:
|
||||
* @plugin: a #MetaPlugin
|
||||
* @grab_window: the X window to grab the keyboard and mouse on
|
||||
* @cursor: the cursor to use for the pointer grab, or None,
|
||||
* to use the normal cursor for the grab window and
|
||||
* its descendants.
|
||||
* @options: flags that modify the behavior of the modal grab
|
||||
* @timestamp: the timestamp used for establishing grabs
|
||||
*
|
||||
@@ -291,15 +285,13 @@ meta_plugin_destroy_completed (MetaPlugin *plugin,
|
||||
*/
|
||||
gboolean
|
||||
meta_plugin_begin_modal (MetaPlugin *plugin,
|
||||
Window grab_window,
|
||||
Cursor cursor,
|
||||
MetaModalOptions options,
|
||||
guint32 timestamp)
|
||||
{
|
||||
MetaPluginPrivate *priv = META_PLUGIN (plugin)->priv;
|
||||
|
||||
return meta_begin_modal_for_plugin (priv->screen, plugin,
|
||||
grab_window, cursor, options, timestamp);
|
||||
options, timestamp);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
44
src/compositor/meta-shaped-texture-private.h
Normal file
44
src/compositor/meta-shaped-texture-private.h
Normal file
@@ -0,0 +1,44 @@
|
||||
/*
|
||||
* shaped texture
|
||||
*
|
||||
* An actor to draw a texture clipped to a list of rectangles
|
||||
*
|
||||
* Authored By Neil Roberts <neil@linux.intel.com>
|
||||
*
|
||||
* Copyright (C) 2008 Intel Corporation
|
||||
* 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_SHAPED_TEXTURE_PRIVATE_H__
|
||||
#define __META_SHAPED_TEXTURE_PRIVATE_H__
|
||||
|
||||
#include <meta/meta-shaped-texture.h>
|
||||
#include "meta-wayland-private.h"
|
||||
|
||||
ClutterActor *meta_shaped_texture_new_with_xwindow (Window xwindow);
|
||||
ClutterActor *meta_shaped_texture_new_with_wayland_surface (MetaWaylandSurface *surface);
|
||||
void meta_shaped_texture_set_wayland_surface (MetaShapedTexture *stex,
|
||||
MetaWaylandSurface *surface);
|
||||
MetaWaylandSurface *meta_shaped_texture_get_wayland_surface (MetaShapedTexture *stex);
|
||||
|
||||
void meta_shaped_texture_set_pixmap (MetaShapedTexture *stex,
|
||||
Pixmap pixmap);
|
||||
void meta_shaped_texture_attach_wayland_buffer (MetaShapedTexture *stex,
|
||||
MetaWaylandBuffer *buffer);
|
||||
|
||||
#endif
|
||||
@@ -30,8 +30,13 @@
|
||||
#include <config.h>
|
||||
|
||||
#include <meta/meta-shaped-texture.h>
|
||||
#include <meta/util.h>
|
||||
#include "meta-texture-tower.h"
|
||||
|
||||
#include "meta-shaped-texture-private.h"
|
||||
#include "meta-wayland-private.h"
|
||||
#include <cogl/cogl-wayland-server.h>
|
||||
|
||||
#include <clutter/clutter.h>
|
||||
#include <cogl/cogl.h>
|
||||
#include <cogl/cogl-texture-pixmap-x11.h>
|
||||
@@ -55,6 +60,13 @@ static void meta_shaped_texture_get_preferred_height (ClutterActor *self,
|
||||
|
||||
static gboolean meta_shaped_texture_get_paint_volume (ClutterActor *self, ClutterPaintVolume *volume);
|
||||
|
||||
typedef enum _MetaShapedTextureType
|
||||
{
|
||||
META_SHAPED_TEXTURE_TYPE_X11_PIXMAP,
|
||||
META_SHAPED_TEXTURE_TYPE_WAYLAND_SURFACE,
|
||||
} MetaShapedTextureType;
|
||||
|
||||
|
||||
G_DEFINE_TYPE (MetaShapedTexture, meta_shaped_texture,
|
||||
CLUTTER_TYPE_ACTOR);
|
||||
|
||||
@@ -65,13 +77,24 @@ G_DEFINE_TYPE (MetaShapedTexture, meta_shaped_texture,
|
||||
struct _MetaShapedTexturePrivate
|
||||
{
|
||||
MetaTextureTower *paint_tower;
|
||||
Pixmap pixmap;
|
||||
CoglTexturePixmapX11 *texture;
|
||||
|
||||
MetaShapedTextureType type;
|
||||
union {
|
||||
struct {
|
||||
Pixmap pixmap;
|
||||
} x11;
|
||||
struct {
|
||||
MetaWaylandSurface *surface;
|
||||
} wayland;
|
||||
};
|
||||
|
||||
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;
|
||||
|
||||
@@ -103,7 +126,10 @@ meta_shaped_texture_init (MetaShapedTexture *self)
|
||||
priv = self->priv = META_SHAPED_TEXTURE_GET_PRIVATE (self);
|
||||
|
||||
priv->paint_tower = meta_texture_tower_new ();
|
||||
|
||||
priv->type = META_SHAPED_TEXTURE_TYPE_X11_PIXMAP;
|
||||
priv->texture = NULL;
|
||||
|
||||
priv->mask_texture = NULL;
|
||||
priv->create_mipmaps = TRUE;
|
||||
}
|
||||
@@ -118,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);
|
||||
@@ -128,19 +152,133 @@ 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)
|
||||
{
|
||||
MetaShapedTexturePrivate *priv;
|
||||
guint width, height;
|
||||
|
||||
g_return_if_fail (META_IS_SHAPED_TEXTURE (stex));
|
||||
|
||||
priv = stex->priv;
|
||||
|
||||
if (priv->texture)
|
||||
cogl_object_unref (priv->texture);
|
||||
|
||||
priv->texture = cogl_tex;
|
||||
|
||||
if (cogl_tex != NULL)
|
||||
{
|
||||
width = cogl_texture_get_width (COGL_TEXTURE (cogl_tex));
|
||||
height = cogl_texture_get_height (COGL_TEXTURE (cogl_tex));
|
||||
|
||||
if (width != priv->tex_width ||
|
||||
height != priv->tex_height)
|
||||
{
|
||||
priv->tex_width = width;
|
||||
priv->tex_height = height;
|
||||
|
||||
clutter_actor_queue_relayout (CLUTTER_ACTOR (stex));
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
/* size changed to 0 going to an invalid handle */
|
||||
priv->tex_width = 0;
|
||||
priv->tex_height = 0;
|
||||
clutter_actor_queue_relayout (CLUTTER_ACTOR (stex));
|
||||
}
|
||||
|
||||
/* NB: We don't queue a redraw of the actor here because we don't
|
||||
* know how much of the buffer has changed with respect to the
|
||||
* previous buffer. We only queue a redraw in response to surface
|
||||
* damage. */
|
||||
}
|
||||
|
||||
static void
|
||||
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;
|
||||
@@ -177,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);
|
||||
}
|
||||
|
||||
@@ -216,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
|
||||
@@ -285,38 +443,61 @@ meta_shaped_texture_pick (ClutterActor *actor,
|
||||
MetaShapedTexture *stex = (MetaShapedTexture *) actor;
|
||||
MetaShapedTexturePrivate *priv = stex->priv;
|
||||
|
||||
if (!clutter_actor_should_pick_paint (actor) ||
|
||||
(priv->clip_region && cairo_region_is_empty (priv->clip_region)))
|
||||
return;
|
||||
|
||||
/* If there is no region then use the regular pick */
|
||||
if (priv->mask_texture == NULL)
|
||||
CLUTTER_ACTOR_CLASS (meta_shaped_texture_parent_class)
|
||||
->pick (actor, color);
|
||||
else if (clutter_actor_should_pick_paint (actor))
|
||||
if (priv->input_shape_region == NULL)
|
||||
CLUTTER_ACTOR_CLASS (meta_shaped_texture_parent_class)->pick (actor, color);
|
||||
else
|
||||
{
|
||||
CoglTexture *paint_tex;
|
||||
ClutterActorBox alloc;
|
||||
guint tex_width, tex_height;
|
||||
int n_rects;
|
||||
float *rectangles;
|
||||
int i;
|
||||
CoglPipeline *pipeline;
|
||||
CoglContext *ctx;
|
||||
CoglFramebuffer *fb;
|
||||
CoglColor cogl_color;
|
||||
|
||||
paint_tex = COGL_TEXTURE (priv->texture);
|
||||
/* Note: We don't bother trying to intersect the pick and clip regions
|
||||
* since needing to copy the region, do the intersection, and probably
|
||||
* increase the number of rectangles seems more likely to have a negative
|
||||
* effect.
|
||||
*
|
||||
* NB: Most of the time when just using rectangles for picking then
|
||||
* picking shouldn't involve any rendering, and minimizing the number of
|
||||
* rectangles has more benefit than reducing the area of the pick
|
||||
* region.
|
||||
*/
|
||||
|
||||
if (paint_tex == NULL)
|
||||
return;
|
||||
n_rects = cairo_region_num_rectangles (priv->input_shape_region);
|
||||
rectangles = g_alloca (sizeof (float) * 4 * n_rects);
|
||||
|
||||
tex_width = cogl_texture_get_width (paint_tex);
|
||||
tex_height = cogl_texture_get_height (paint_tex);
|
||||
for (i = 0; i < n_rects; i++)
|
||||
{
|
||||
cairo_rectangle_int_t rect;
|
||||
int pos = i * 4;
|
||||
|
||||
if (tex_width == 0 || tex_height == 0) /* no contents yet */
|
||||
return;
|
||||
cairo_region_get_rectangle (priv->input_shape_region, i, &rect);
|
||||
|
||||
cogl_set_source_color4ub (color->red, color->green, color->blue,
|
||||
color->alpha);
|
||||
rectangles[pos] = rect.x;
|
||||
rectangles[pos + 1] = rect.y;
|
||||
rectangles[pos + 2] = rect.x + rect.width;
|
||||
rectangles[pos + 3] = rect.y + rect.height;
|
||||
}
|
||||
|
||||
clutter_actor_get_allocation_box (actor, &alloc);
|
||||
ctx = clutter_backend_get_cogl_context (clutter_get_default_backend ());
|
||||
fb = cogl_get_draw_framebuffer ();
|
||||
|
||||
/* Paint the mask rectangle in the given color */
|
||||
cogl_set_source_texture (priv->mask_texture);
|
||||
cogl_rectangle_with_texture_coords (0, 0,
|
||||
alloc.x2 - alloc.x1,
|
||||
alloc.y2 - alloc.y1,
|
||||
0, 0, 1, 1);
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -366,11 +547,45 @@ meta_shaped_texture_get_paint_volume (ClutterActor *self,
|
||||
}
|
||||
|
||||
ClutterActor *
|
||||
meta_shaped_texture_new (void)
|
||||
meta_shaped_texture_new_with_wayland_surface (MetaWaylandSurface *surface)
|
||||
{
|
||||
ClutterActor *self = g_object_new (META_TYPE_SHAPED_TEXTURE, NULL);
|
||||
ClutterActor *actor = g_object_new (META_TYPE_SHAPED_TEXTURE, NULL);
|
||||
MetaShapedTexturePrivate *priv = META_SHAPED_TEXTURE (actor)->priv;
|
||||
|
||||
return self;
|
||||
/* XXX: it could probably be better to have a "type" construct-only
|
||||
* property or create wayland/x11 subclasses */
|
||||
priv->type = META_SHAPED_TEXTURE_TYPE_WAYLAND_SURFACE;
|
||||
|
||||
meta_shaped_texture_set_wayland_surface (META_SHAPED_TEXTURE (actor),
|
||||
surface);
|
||||
|
||||
return actor;
|
||||
}
|
||||
|
||||
void
|
||||
meta_shaped_texture_set_wayland_surface (MetaShapedTexture *stex,
|
||||
MetaWaylandSurface *surface)
|
||||
{
|
||||
MetaShapedTexturePrivate *priv = stex->priv;
|
||||
|
||||
priv->wayland.surface = surface;
|
||||
|
||||
if (surface && surface->buffer_ref.buffer)
|
||||
meta_shaped_texture_attach_wayland_buffer (stex,
|
||||
surface->buffer_ref.buffer);
|
||||
}
|
||||
|
||||
MetaWaylandSurface *
|
||||
meta_shaped_texture_get_wayland_surface (MetaShapedTexture *stex)
|
||||
{
|
||||
MetaShapedTexturePrivate *priv = stex->priv;
|
||||
return priv->wayland.surface;
|
||||
}
|
||||
|
||||
ClutterActor *
|
||||
meta_shaped_texture_new_with_xwindow (Window xwindow)
|
||||
{
|
||||
return g_object_new (META_TYPE_SHAPED_TEXTURE, NULL);
|
||||
}
|
||||
|
||||
void
|
||||
@@ -389,8 +604,7 @@ meta_shaped_texture_set_create_mipmaps (MetaShapedTexture *stex,
|
||||
{
|
||||
CoglTexture *base_texture;
|
||||
priv->create_mipmaps = create_mipmaps;
|
||||
base_texture = create_mipmaps ?
|
||||
COGL_TEXTURE (priv->texture) : NULL;
|
||||
base_texture = create_mipmaps ? priv->texture : NULL;
|
||||
meta_texture_tower_set_base_texture (priv->paint_tower, base_texture);
|
||||
}
|
||||
}
|
||||
@@ -416,74 +630,190 @@ meta_shaped_texture_set_mask_texture (MetaShapedTexture *stex,
|
||||
clutter_actor_queue_redraw (CLUTTER_ACTOR (stex));
|
||||
}
|
||||
|
||||
void
|
||||
static void
|
||||
wayland_surface_update_area (MetaShapedTexture *stex,
|
||||
int x,
|
||||
int y,
|
||||
int width,
|
||||
int height)
|
||||
{
|
||||
MetaShapedTexturePrivate *priv;
|
||||
MetaWaylandBuffer *buffer;
|
||||
|
||||
priv = stex->priv;
|
||||
|
||||
g_return_if_fail (priv->type == META_SHAPED_TEXTURE_TYPE_WAYLAND_SURFACE);
|
||||
g_return_if_fail (priv->texture != NULL);
|
||||
|
||||
buffer = priv->wayland.surface->buffer_ref.buffer;
|
||||
|
||||
if (buffer)
|
||||
{
|
||||
struct wl_resource *resource = buffer->resource;
|
||||
struct wl_shm_buffer *shm_buffer = wl_shm_buffer_get (resource);
|
||||
|
||||
if (shm_buffer)
|
||||
{
|
||||
CoglPixelFormat format;
|
||||
|
||||
switch (wl_shm_buffer_get_format (shm_buffer))
|
||||
{
|
||||
#if G_BYTE_ORDER == G_BIG_ENDIAN
|
||||
case WL_SHM_FORMAT_ARGB8888:
|
||||
format = COGL_PIXEL_FORMAT_ARGB_8888_PRE;
|
||||
break;
|
||||
case WL_SHM_FORMAT_XRGB8888:
|
||||
format = COGL_PIXEL_FORMAT_ARGB_8888;
|
||||
break;
|
||||
#elif G_BYTE_ORDER == G_LITTLE_ENDIAN
|
||||
case WL_SHM_FORMAT_ARGB8888:
|
||||
format = COGL_PIXEL_FORMAT_BGRA_8888_PRE;
|
||||
break;
|
||||
case WL_SHM_FORMAT_XRGB8888:
|
||||
format = COGL_PIXEL_FORMAT_BGRA_8888;
|
||||
break;
|
||||
#endif
|
||||
default:
|
||||
g_warn_if_reached ();
|
||||
format = COGL_PIXEL_FORMAT_ARGB_8888;
|
||||
}
|
||||
|
||||
cogl_texture_set_region (priv->texture,
|
||||
x, y,
|
||||
x, y,
|
||||
width, height,
|
||||
width, height,
|
||||
format,
|
||||
wl_shm_buffer_get_stride (shm_buffer),
|
||||
wl_shm_buffer_get_data (shm_buffer));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
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;
|
||||
|
||||
/* 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
|
||||
* actor coordinates...
|
||||
*/
|
||||
|
||||
/* Calling clutter_actor_get_allocation_box() is enormously expensive
|
||||
* if the actor has an out-of-date allocation, since it triggers
|
||||
* a full redraw. clutter_actor_queue_redraw_with_clip() would redraw
|
||||
* the whole stage anyways in that case, so just go ahead and do
|
||||
* it here.
|
||||
*/
|
||||
if (!clutter_actor_has_allocation (self))
|
||||
{
|
||||
clutter_actor_queue_redraw (self);
|
||||
return;
|
||||
}
|
||||
|
||||
priv = stex->priv;
|
||||
|
||||
if (priv->tex_width == 0 || priv->tex_height == 0)
|
||||
return;
|
||||
|
||||
clutter_actor_get_allocation_box (self, &allocation);
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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 height,
|
||||
cairo_region_t *unobscured_region)
|
||||
{
|
||||
MetaShapedTexturePrivate *priv;
|
||||
const cairo_rectangle_int_t clip = { x, y, width, height };
|
||||
cairo_rectangle_int_t clip;
|
||||
|
||||
priv = stex->priv;
|
||||
|
||||
if (priv->texture == NULL)
|
||||
return;
|
||||
return FALSE;
|
||||
|
||||
cogl_texture_pixmap_x11_update_area (priv->texture,
|
||||
x, y, width, height);
|
||||
switch (priv->type)
|
||||
{
|
||||
case META_SHAPED_TEXTURE_TYPE_X11_PIXMAP:
|
||||
cogl_texture_pixmap_x11_update_area (COGL_TEXTURE_PIXMAP_X11 (priv->texture),
|
||||
x, y, width, height);
|
||||
break;
|
||||
case META_SHAPED_TEXTURE_TYPE_WAYLAND_SURFACE:
|
||||
wayland_surface_update_area (stex, x, y, width, height);
|
||||
break;
|
||||
}
|
||||
|
||||
meta_texture_tower_update_area (priv->paint_tower, x, y, width, height);
|
||||
|
||||
clutter_actor_queue_redraw_with_clip (CLUTTER_ACTOR (stex), &clip);
|
||||
}
|
||||
get_clip (stex, x, y, width, height, &clip);
|
||||
|
||||
static void
|
||||
set_cogl_texture (MetaShapedTexture *stex,
|
||||
CoglTexturePixmapX11 *cogl_tex)
|
||||
{
|
||||
MetaShapedTexturePrivate *priv;
|
||||
guint width, height;
|
||||
|
||||
g_return_if_fail (META_IS_SHAPED_TEXTURE (stex));
|
||||
|
||||
priv = stex->priv;
|
||||
|
||||
if (priv->texture != NULL)
|
||||
cogl_object_unref (priv->texture);
|
||||
|
||||
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)
|
||||
if (unobscured_region)
|
||||
{
|
||||
width = cogl_texture_get_width (COGL_TEXTURE (cogl_tex));
|
||||
height = cogl_texture_get_height (COGL_TEXTURE (cogl_tex));
|
||||
cairo_region_t *intersection;
|
||||
|
||||
if (width != priv->tex_width ||
|
||||
height != priv->tex_height)
|
||||
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))
|
||||
{
|
||||
priv->tex_width = width;
|
||||
priv->tex_height = height;
|
||||
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);
|
||||
|
||||
clutter_actor_queue_relayout (CLUTTER_ACTOR (stex));
|
||||
return TRUE;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
/* size changed to 0 going to an inavlid texture */
|
||||
priv->tex_width = 0;
|
||||
priv->tex_height = 0;
|
||||
clutter_actor_queue_relayout (CLUTTER_ACTOR (stex));
|
||||
|
||||
cairo_region_destroy (intersection);
|
||||
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
clutter_actor_queue_redraw (CLUTTER_ACTOR (stex));
|
||||
clutter_actor_queue_redraw_with_clip (CLUTTER_ACTOR (stex), &clip);
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -501,16 +831,64 @@ meta_shaped_texture_set_pixmap (MetaShapedTexture *stex,
|
||||
|
||||
priv = stex->priv;
|
||||
|
||||
if (priv->pixmap == pixmap)
|
||||
if (priv->x11.pixmap == pixmap)
|
||||
return;
|
||||
|
||||
priv->pixmap = pixmap;
|
||||
priv->x11.pixmap = pixmap;
|
||||
|
||||
if (pixmap != None)
|
||||
{
|
||||
CoglContext *ctx =
|
||||
clutter_backend_get_cogl_context (clutter_get_default_backend ());
|
||||
set_cogl_texture (stex, cogl_texture_pixmap_x11_new (ctx, pixmap, FALSE, NULL));
|
||||
CoglTexture *texture =
|
||||
COGL_TEXTURE (cogl_texture_pixmap_x11_new (ctx, pixmap, FALSE, NULL));
|
||||
set_cogl_texture (stex, texture);
|
||||
}
|
||||
else
|
||||
set_cogl_texture (stex, NULL);
|
||||
|
||||
if (priv->create_mipmaps)
|
||||
meta_texture_tower_set_base_texture (priv->paint_tower,
|
||||
COGL_TEXTURE (priv->texture));
|
||||
}
|
||||
|
||||
void
|
||||
meta_shaped_texture_attach_wayland_buffer (MetaShapedTexture *stex,
|
||||
MetaWaylandBuffer *buffer)
|
||||
{
|
||||
MetaShapedTexturePrivate *priv;
|
||||
|
||||
g_return_if_fail (META_IS_SHAPED_TEXTURE (stex));
|
||||
|
||||
priv = stex->priv;
|
||||
|
||||
/* TODO: we should change this api to be something like
|
||||
* meta_shaped_texture_notify_buffer_attach() since we now maintain
|
||||
* a reference to the MetaWaylandSurface where we can access the
|
||||
* buffer without it being explicitly passed as an argument.
|
||||
*/
|
||||
g_return_if_fail (priv->wayland.surface->buffer_ref.buffer == buffer);
|
||||
|
||||
if (buffer)
|
||||
{
|
||||
CoglContext *ctx =
|
||||
clutter_backend_get_cogl_context (clutter_get_default_backend ());
|
||||
CoglError *catch_error = NULL;
|
||||
CoglTexture *texture =
|
||||
COGL_TEXTURE (cogl_wayland_texture_2d_new_from_buffer (ctx,
|
||||
buffer->resource,
|
||||
&catch_error));
|
||||
if (!texture)
|
||||
{
|
||||
cogl_error_free (catch_error);
|
||||
}
|
||||
else
|
||||
{
|
||||
buffer->width = cogl_texture_get_width (texture);
|
||||
buffer->height = cogl_texture_get_height (texture);
|
||||
}
|
||||
|
||||
set_cogl_texture (stex, texture);
|
||||
}
|
||||
else
|
||||
set_cogl_texture (stex, NULL);
|
||||
@@ -533,11 +911,46 @@ meta_shaped_texture_get_texture (MetaShapedTexture *stex)
|
||||
return COGL_TEXTURE (stex->priv->texture);
|
||||
}
|
||||
|
||||
/**
|
||||
* meta_shaped_texture_set_input_shape_region:
|
||||
* @stex: a #MetaShapedTexture
|
||||
* @shape_region: the region of the texture that should respond to
|
||||
* input.
|
||||
*
|
||||
* Determines what region of the texture should accept input. For
|
||||
* X based windows this is defined by the ShapeInput region of the
|
||||
* window.
|
||||
*/
|
||||
void
|
||||
meta_shaped_texture_set_input_shape_region (MetaShapedTexture *stex,
|
||||
cairo_region_t *shape_region)
|
||||
{
|
||||
MetaShapedTexturePrivate *priv;
|
||||
|
||||
g_return_if_fail (META_IS_SHAPED_TEXTURE (stex));
|
||||
|
||||
priv = stex->priv;
|
||||
|
||||
if (priv->input_shape_region != NULL)
|
||||
{
|
||||
cairo_region_destroy (priv->input_shape_region);
|
||||
priv->input_shape_region = NULL;
|
||||
}
|
||||
|
||||
if (shape_region != NULL)
|
||||
{
|
||||
cairo_region_reference (shape_region);
|
||||
priv->input_shape_region = shape_region;
|
||||
}
|
||||
|
||||
clutter_actor_queue_redraw (CLUTTER_ACTOR (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
|
||||
@@ -558,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);
|
||||
@@ -569,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
|
||||
|
||||
@@ -5,6 +5,9 @@
|
||||
|
||||
#include <config.h>
|
||||
|
||||
#include <wayland-server.h>
|
||||
#include <meta-wayland-private.h>
|
||||
|
||||
#include <X11/extensions/Xdamage.h>
|
||||
#include <meta/compositor-mutter.h>
|
||||
|
||||
@@ -24,8 +27,18 @@ void meta_window_actor_unmaximize (MetaWindowActor *self,
|
||||
MetaRectangle *old_rect,
|
||||
MetaRectangle *new_rect);
|
||||
|
||||
void meta_window_actor_process_damage (MetaWindowActor *self,
|
||||
XDamageNotifyEvent *event);
|
||||
void meta_window_actor_process_x11_damage (MetaWindowActor *self,
|
||||
XDamageNotifyEvent *event);
|
||||
|
||||
void meta_window_actor_process_wayland_damage (MetaWindowActor *self,
|
||||
int x,
|
||||
int y,
|
||||
int width,
|
||||
int height);
|
||||
void meta_window_actor_set_wayland_surface (MetaWindowActor *self,
|
||||
MetaWaylandSurface *surface);
|
||||
void meta_window_actor_attach_wayland_buffer (MetaWindowActor *self,
|
||||
MetaWaylandBuffer *buffer);
|
||||
|
||||
void meta_window_actor_pre_paint (MetaWindowActor *self);
|
||||
void meta_window_actor_post_paint (MetaWindowActor *self);
|
||||
@@ -57,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);
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -13,6 +13,7 @@
|
||||
#include "meta-window-group.h"
|
||||
#include "meta-background-actor-private.h"
|
||||
#include "meta-background-group-private.h"
|
||||
#include "window-private.h"
|
||||
|
||||
struct _MetaWindowGroupClass
|
||||
{
|
||||
@@ -89,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 = 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
|
||||
@@ -124,25 +138,35 @@ 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 (info->unredirected_window != NULL)
|
||||
if (!meta_is_wayland_compositor ())
|
||||
{
|
||||
cairo_rectangle_int_t unredirected_rect;
|
||||
MetaWindow *window = meta_window_actor_get_meta_window (info->unredirected_window);
|
||||
info = meta_screen_get_compositor_data (window_group->screen);
|
||||
if (info->unredirected_window != NULL)
|
||||
{
|
||||
cairo_rectangle_int_t unredirected_rect;
|
||||
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);
|
||||
meta_window_get_outer_rect (window, (MetaRectangle *)&unredirected_rect);
|
||||
cairo_region_subtract_rectangle (unobscured_region, &unredirected_rect);
|
||||
cairo_region_subtract_rectangle (clip_region, &unredirected_rect);
|
||||
}
|
||||
}
|
||||
|
||||
/* We walk the list from top to bottom (opposite of painting order),
|
||||
@@ -155,7 +179,8 @@ meta_window_group_paint (ClutterActor *actor)
|
||||
if (!CLUTTER_ACTOR_IS_VISIBLE (child))
|
||||
continue;
|
||||
|
||||
if (info->unredirected_window != NULL &&
|
||||
if (!meta_is_wayland_compositor () &&
|
||||
info->unredirected_window != NULL &&
|
||||
child == CLUTTER_ACTOR (info->unredirected_window))
|
||||
continue;
|
||||
|
||||
@@ -189,20 +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);
|
||||
|
||||
meta_window_actor_set_visible_region (window_actor, visible_region);
|
||||
/* Temporarily move to the coordinate system of the actor */
|
||||
cairo_region_translate (unobscured_region, - x, - y);
|
||||
cairo_region_translate (clip_region, - x, - y);
|
||||
|
||||
meta_window_actor_set_unobscured_region (window_actor, unobscured_region);
|
||||
meta_window_actor_set_clip_region (window_actor, clip_region);
|
||||
|
||||
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))
|
||||
@@ -215,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);
|
||||
|
||||
@@ -238,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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -269,6 +270,8 @@ meta_core_lower_beneath_grab_window (Display *xdisplay,
|
||||
MetaDisplay *display;
|
||||
MetaScreen *screen;
|
||||
MetaWindow *grab_window;
|
||||
MetaStackWindow stack_window;
|
||||
MetaStackWindow stack_sibling;
|
||||
|
||||
display = meta_display_for_x_display (xdisplay);
|
||||
screen = meta_display_screen_for_xwindow (display, xwindow);
|
||||
@@ -281,9 +284,13 @@ meta_core_lower_beneath_grab_window (Display *xdisplay,
|
||||
changes.sibling = grab_window->frame ? grab_window->frame->xwindow
|
||||
: grab_window->xwindow;
|
||||
|
||||
stack_window.any.type = META_WINDOW_CLIENT_TYPE_X11;
|
||||
stack_window.x11.xwindow = xwindow;
|
||||
stack_sibling.any.type = META_WINDOW_CLIENT_TYPE_X11;
|
||||
stack_sibling.x11.xwindow = changes.sibling;
|
||||
meta_stack_tracker_record_lower_below (screen->stack_tracker,
|
||||
xwindow,
|
||||
changes.sibling,
|
||||
&stack_window,
|
||||
&stack_sibling,
|
||||
XNextRequest (screen->display->xdisplay));
|
||||
|
||||
meta_error_trap_push (display);
|
||||
|
||||
@@ -86,6 +86,14 @@ typedef enum {
|
||||
META_TILE_MAXIMIZED
|
||||
} MetaTileMode;
|
||||
|
||||
typedef enum {
|
||||
META_FOCUS_NONE = 0,
|
||||
META_FOCUS_X_CLIENT = 1,
|
||||
META_FOCUS_WAYLAND_CLIENT = 2,
|
||||
META_FOCUS_NO_FOCUS_WINDOW = 3,
|
||||
META_FOCUS_STAGE = 4
|
||||
} MetaFocusType;
|
||||
|
||||
struct _MetaDisplay
|
||||
{
|
||||
GObject parent_instance;
|
||||
@@ -117,6 +125,7 @@ struct _MetaDisplay
|
||||
* like the no_focus_window or the stage X window. */
|
||||
Window focus_xwindow;
|
||||
gulong focus_serial;
|
||||
MetaFocusType focus_type;
|
||||
|
||||
/* last timestamp passed to XSetInputFocus */
|
||||
guint32 last_focus_time;
|
||||
@@ -467,14 +476,18 @@ gboolean meta_display_modifiers_accelerator_activate (MetaDisplay *display);
|
||||
/* In above-tab-keycode.c */
|
||||
guint meta_display_get_above_tab_keycode (MetaDisplay *display);
|
||||
|
||||
gboolean meta_display_handle_event (MetaDisplay *display,
|
||||
XEvent *event);
|
||||
|
||||
#ifdef HAVE_XI23
|
||||
gboolean meta_display_process_barrier_event (MetaDisplay *display,
|
||||
XIBarrierEvent *event);
|
||||
#endif /* HAVE_XI23 */
|
||||
|
||||
void meta_display_set_input_focus_xwindow (MetaDisplay *display,
|
||||
MetaScreen *screen,
|
||||
Window window,
|
||||
guint32 timestamp);
|
||||
void meta_display_set_input_focus_xwindow (MetaDisplay *display,
|
||||
MetaScreen *screen,
|
||||
MetaFocusType type,
|
||||
Window window,
|
||||
guint32 timestamp);
|
||||
|
||||
#endif
|
||||
|
||||
@@ -53,6 +53,7 @@
|
||||
#include <X11/Xatom.h>
|
||||
#include <X11/cursorfont.h>
|
||||
#include "mutter-enum-types.h"
|
||||
#include "meta-idle-monitor-private.h"
|
||||
|
||||
#ifdef HAVE_RANDR
|
||||
#include <X11/extensions/Xrandr.h>
|
||||
@@ -74,6 +75,8 @@
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include "meta-xwayland-private.h"
|
||||
|
||||
#define GRAB_OP_IS_WINDOW_SWITCH(g) \
|
||||
(g == META_GRAB_OP_KEYBOARD_TABBING_NORMAL || \
|
||||
g == META_GRAB_OP_KEYBOARD_TABBING_DOCK || \
|
||||
@@ -515,6 +518,9 @@ meta_display_open (void)
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
if (meta_is_wayland_compositor ())
|
||||
meta_xwayland_complete_init ();
|
||||
|
||||
if (meta_is_syncing ())
|
||||
XSynchronize (xdisplay, True);
|
||||
|
||||
@@ -931,8 +937,24 @@ meta_display_open (void)
|
||||
while (tmp != NULL)
|
||||
{
|
||||
MetaScreen *screen = tmp->data;
|
||||
|
||||
meta_screen_manage_all_windows (screen);
|
||||
|
||||
if (meta_is_wayland_compositor ())
|
||||
{
|
||||
/* Instead of explicitly enumerating all windows during
|
||||
* initialization, when we run as a wayland compositor we can rely on
|
||||
* xwayland notifying us of all top level windows so we create
|
||||
* MetaWindows when we get those notifications.
|
||||
*
|
||||
* We still want a guard window so we can avoid
|
||||
* unmapping/withdrawing minimized windows for live
|
||||
* thumbnails...
|
||||
*/
|
||||
if (screen->guard_window == None)
|
||||
screen->guard_window =
|
||||
meta_screen_create_guard_window (screen->display->xdisplay, screen);
|
||||
}
|
||||
else
|
||||
meta_screen_manage_all_windows (screen);
|
||||
|
||||
tmp = tmp->next;
|
||||
}
|
||||
@@ -974,8 +996,10 @@ meta_display_open (void)
|
||||
|
||||
meta_error_trap_pop (the_display);
|
||||
}
|
||||
|
||||
meta_display_ungrab (the_display);
|
||||
|
||||
meta_idle_monitor_init_dbus ();
|
||||
|
||||
meta_display_ungrab (the_display);
|
||||
|
||||
/* Done opening new display */
|
||||
the_display->display_opening = FALSE;
|
||||
@@ -1852,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)
|
||||
@@ -1881,14 +1908,19 @@ get_input_event (MetaDisplay *display,
|
||||
}
|
||||
|
||||
static void
|
||||
update_focus_window (MetaDisplay *display,
|
||||
MetaWindow *window,
|
||||
Window xwindow,
|
||||
gulong serial)
|
||||
update_focus_window (MetaDisplay *display,
|
||||
MetaFocusType type,
|
||||
MetaWindow *window,
|
||||
Window xwindow,
|
||||
gulong serial)
|
||||
{
|
||||
MetaWaylandCompositor *compositor;
|
||||
|
||||
display->focus_serial = serial;
|
||||
|
||||
if (display->focus_xwindow == xwindow)
|
||||
if (display->focus_xwindow == xwindow &&
|
||||
display->focus_type == type &&
|
||||
display->focus_window == window)
|
||||
return;
|
||||
|
||||
if (display->focus_window)
|
||||
@@ -1910,6 +1942,7 @@ update_focus_window (MetaDisplay *display,
|
||||
meta_window_set_focused_internal (previous, FALSE);
|
||||
}
|
||||
|
||||
display->focus_type = type;
|
||||
display->focus_window = window;
|
||||
display->focus_xwindow = xwindow;
|
||||
|
||||
@@ -1922,6 +1955,19 @@ update_focus_window (MetaDisplay *display,
|
||||
else
|
||||
meta_topic (META_DEBUG_FOCUS, "* Focus --> NULL with serial %lu\n", serial);
|
||||
|
||||
if (meta_is_wayland_compositor ())
|
||||
{
|
||||
compositor = meta_wayland_compositor_get_default ();
|
||||
|
||||
if (display->focus_type == META_FOCUS_NO_FOCUS_WINDOW ||
|
||||
display->focus_type == META_FOCUS_STAGE)
|
||||
meta_wayland_compositor_set_input_focus (compositor, NULL);
|
||||
else if (window && window->surface)
|
||||
meta_wayland_compositor_set_input_focus (compositor, window);
|
||||
else
|
||||
meta_topic (META_DEBUG_FOCUS, "Focus change has no effect, because there is no matching wayland surface");
|
||||
}
|
||||
|
||||
g_object_notify (G_OBJECT (display), "focus-window");
|
||||
meta_display_update_active_window_hint (display);
|
||||
}
|
||||
@@ -1956,19 +2002,18 @@ timestamp_too_old (MetaDisplay *display,
|
||||
}
|
||||
|
||||
static void
|
||||
request_xserver_input_focus_change (MetaDisplay *display,
|
||||
MetaScreen *screen,
|
||||
Window xwindow,
|
||||
guint32 timestamp)
|
||||
request_xserver_input_focus_change (MetaDisplay *display,
|
||||
MetaScreen *screen,
|
||||
MetaFocusType type,
|
||||
MetaWindow *meta_window,
|
||||
Window xwindow,
|
||||
guint32 timestamp)
|
||||
{
|
||||
MetaWindow *meta_window;
|
||||
gulong serial;
|
||||
|
||||
if (timestamp_too_old (display, ×tamp))
|
||||
return;
|
||||
|
||||
meta_window = meta_display_lookup_x_window (display, xwindow);
|
||||
|
||||
meta_error_trap_push (display);
|
||||
|
||||
/* In order for mutter to know that the focus request succeeded, we track
|
||||
@@ -1995,6 +2040,7 @@ request_xserver_input_focus_change (MetaDisplay *display,
|
||||
meta_display_ungrab (display);
|
||||
|
||||
update_focus_window (display,
|
||||
type,
|
||||
meta_window,
|
||||
xwindow,
|
||||
serial);
|
||||
@@ -2015,9 +2061,12 @@ handle_window_focus_event (MetaDisplay *display,
|
||||
unsigned long serial)
|
||||
{
|
||||
MetaWindow *focus_window;
|
||||
MetaFocusType type;
|
||||
#ifdef WITH_VERBOSE_MODE
|
||||
const char *window_type;
|
||||
|
||||
type = META_FOCUS_NONE;
|
||||
|
||||
/* Note the event can be on either the window or the frame,
|
||||
* we focus the frame for shaded windows
|
||||
*/
|
||||
@@ -2029,14 +2078,26 @@ handle_window_focus_event (MetaDisplay *display,
|
||||
window_type = "frame window";
|
||||
else
|
||||
window_type = "unknown client window";
|
||||
|
||||
if (window->client_type == META_WINDOW_CLIENT_TYPE_WAYLAND)
|
||||
type = META_FOCUS_WAYLAND_CLIENT;
|
||||
else
|
||||
type = META_FOCUS_X_CLIENT;
|
||||
}
|
||||
else if (meta_display_xwindow_is_a_no_focus_window (display, event->event))
|
||||
window_type = "no_focus_window";
|
||||
{
|
||||
window_type = "no_focus_window";
|
||||
type = META_FOCUS_NO_FOCUS_WINDOW;
|
||||
}
|
||||
else if (meta_display_screen_for_root (display, event->event))
|
||||
window_type = "root window";
|
||||
else
|
||||
window_type = "unknown window";
|
||||
|
||||
/* Don't change type if we don't know the new window */
|
||||
if (type == META_FOCUS_NONE)
|
||||
type = display->focus_type;
|
||||
|
||||
meta_topic (META_DEBUG_FOCUS,
|
||||
"Focus %s event received on %s 0x%lx (%s) "
|
||||
"mode %s detail %s serial %lu\n",
|
||||
@@ -2114,6 +2175,7 @@ handle_window_focus_event (MetaDisplay *display,
|
||||
if (display->server_focus_serial > display->focus_serial)
|
||||
{
|
||||
update_focus_window (display,
|
||||
type,
|
||||
focus_window,
|
||||
focus_window ? focus_window->xwindow : None,
|
||||
display->server_focus_serial);
|
||||
@@ -2121,10 +2183,9 @@ handle_window_focus_event (MetaDisplay *display,
|
||||
}
|
||||
|
||||
/**
|
||||
* event_callback:
|
||||
* meta_display_handle_event:
|
||||
* @display: The MetaDisplay that events are coming from
|
||||
* @event: The event that just happened
|
||||
* @data: The #MetaDisplay that events are coming from, cast to a gpointer
|
||||
* so that it can be sent to a callback
|
||||
*
|
||||
* This is the most important function in the whole program. It is the heart,
|
||||
* it is the nexus, it is the Grand Central Station of Mutter's world.
|
||||
@@ -2134,22 +2195,20 @@ handle_window_focus_event (MetaDisplay *display,
|
||||
* busy around here. Most of this function is a ginormous switch statement
|
||||
* dealing with all the kinds of events that might turn up.
|
||||
*/
|
||||
static gboolean
|
||||
event_callback (XEvent *event,
|
||||
gpointer data)
|
||||
gboolean
|
||||
meta_display_handle_event (MetaDisplay *display,
|
||||
XEvent *event)
|
||||
{
|
||||
MetaWindow *window;
|
||||
MetaWindow *property_for_window;
|
||||
MetaDisplay *display;
|
||||
Window modified;
|
||||
gboolean frame_was_receiver;
|
||||
gboolean bypass_compositor;
|
||||
gboolean filter_out_event;
|
||||
XIEvent *input_event;
|
||||
MetaMonitorManager *monitor;
|
||||
MetaScreen *screen;
|
||||
|
||||
display = data;
|
||||
|
||||
#ifdef WITH_VERBOSE_MODE
|
||||
if (dump_events)
|
||||
meta_spew_event (display, event);
|
||||
@@ -2179,11 +2238,19 @@ event_callback (XEvent *event,
|
||||
meta_topic (META_DEBUG_FOCUS, "Earlier attempt to focus %s failed\n",
|
||||
display->focus_window->desc);
|
||||
update_focus_window (display,
|
||||
META_FOCUS_NONE,
|
||||
meta_display_lookup_x_window (display, display->server_focus_window),
|
||||
display->server_focus_window,
|
||||
display->server_focus_serial);
|
||||
}
|
||||
|
||||
screen = meta_display_screen_for_root (display, event->xany.window);
|
||||
if (screen)
|
||||
{
|
||||
if (meta_screen_handle_xevent (screen, event))
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
modified = event_get_modified_window (display, event);
|
||||
|
||||
input_event = get_input_event (display, event);
|
||||
@@ -2256,6 +2323,8 @@ event_callback (XEvent *event,
|
||||
meta_window_update_sync_request_counter (alarm_window, new_counter_value);
|
||||
filter_out_event = TRUE; /* GTK doesn't want to see this really */
|
||||
}
|
||||
else
|
||||
meta_idle_monitor_handle_xevent_all (event);
|
||||
}
|
||||
#endif /* HAVE_XSYNC */
|
||||
|
||||
@@ -2270,32 +2339,9 @@ event_callback (XEvent *event,
|
||||
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_shape_changed (display->compositor,
|
||||
window);
|
||||
}
|
||||
meta_window_update_shape_region_x11 (window);
|
||||
else if (sev->kind == ShapeInput)
|
||||
meta_window_update_input_region_x11 (window);
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -3125,6 +3171,30 @@ event_callback (XEvent *event,
|
||||
return filter_out_event;
|
||||
}
|
||||
|
||||
static gboolean
|
||||
event_callback (XEvent *event,
|
||||
gpointer data)
|
||||
{
|
||||
MetaDisplay *display = data;
|
||||
|
||||
/* Under Wayland we want to filter out mouse motion events so we can
|
||||
synthesize them from the Clutter events instead. This is
|
||||
necessary because the position in the mouse events is passed to
|
||||
the X server relative to the position of the surface. The X
|
||||
server then translates these back to screen coordinates based on
|
||||
the window position. If we rely on this translatation when
|
||||
dragging a window around then the window will jump around
|
||||
erratically because of the lag between updating the window
|
||||
position from the surface position. Instead we bypass the
|
||||
translation altogether by directly using the Clutter events */
|
||||
if (meta_is_wayland_compositor () &&
|
||||
event->type == GenericEvent &&
|
||||
event->xcookie.evtype == XI_Motion)
|
||||
return FALSE;
|
||||
|
||||
return meta_display_handle_event (display, event);
|
||||
}
|
||||
|
||||
/* Return the window this has to do with, if any, rather
|
||||
* than the frame or root window that was selecting
|
||||
* for substructure
|
||||
@@ -5776,6 +5846,9 @@ meta_display_set_input_focus_window (MetaDisplay *display,
|
||||
{
|
||||
request_xserver_input_focus_change (display,
|
||||
window->screen,
|
||||
window->client_type == META_WINDOW_CLIENT_TYPE_WAYLAND ?
|
||||
META_FOCUS_WAYLAND_CLIENT : META_FOCUS_X_CLIENT,
|
||||
window,
|
||||
focus_frame ? window->frame->xwindow : window->xwindow,
|
||||
timestamp);
|
||||
}
|
||||
@@ -5816,13 +5889,16 @@ meta_display_request_take_focus (MetaDisplay *display,
|
||||
}
|
||||
|
||||
void
|
||||
meta_display_set_input_focus_xwindow (MetaDisplay *display,
|
||||
MetaScreen *screen,
|
||||
Window window,
|
||||
guint32 timestamp)
|
||||
meta_display_set_input_focus_xwindow (MetaDisplay *display,
|
||||
MetaScreen *screen,
|
||||
MetaFocusType type,
|
||||
Window window,
|
||||
guint32 timestamp)
|
||||
{
|
||||
request_xserver_input_focus_change (display,
|
||||
screen,
|
||||
type,
|
||||
NULL,
|
||||
window,
|
||||
timestamp);
|
||||
}
|
||||
@@ -5834,6 +5910,8 @@ meta_display_focus_the_no_focus_window (MetaDisplay *display,
|
||||
{
|
||||
request_xserver_input_focus_change (display,
|
||||
screen,
|
||||
META_FOCUS_NO_FOCUS_WINDOW,
|
||||
NULL,
|
||||
screen->no_focus_window,
|
||||
timestamp);
|
||||
}
|
||||
|
||||
@@ -47,6 +47,7 @@ meta_window_ensure_frame (MetaWindow *window)
|
||||
XSetWindowAttributes attrs;
|
||||
Visual *visual;
|
||||
gulong create_serial;
|
||||
MetaStackWindow stack_window;
|
||||
|
||||
if (window->frame)
|
||||
return;
|
||||
@@ -105,8 +106,10 @@ meta_window_ensure_frame (MetaWindow *window)
|
||||
frame->rect.height,
|
||||
frame->window->screen->number,
|
||||
&create_serial);
|
||||
stack_window.any.type = META_WINDOW_CLIENT_TYPE_X11;
|
||||
stack_window.x11.xwindow = frame->xwindow;
|
||||
meta_stack_tracker_record_add (window->screen->stack_tracker,
|
||||
frame->xwindow,
|
||||
&stack_window,
|
||||
create_serial);
|
||||
|
||||
meta_verbose ("Frame for %s is 0x%lx\n", frame->window->desc, frame->xwindow);
|
||||
@@ -138,8 +141,9 @@ meta_window_ensure_frame (MetaWindow *window)
|
||||
window->rect.x = 0;
|
||||
window->rect.y = 0;
|
||||
|
||||
stack_window.x11.xwindow = window->xwindow;
|
||||
meta_stack_tracker_record_remove (window->screen->stack_tracker,
|
||||
window->xwindow,
|
||||
&stack_window,
|
||||
XNextRequest (window->display->xdisplay));
|
||||
XReparentWindow (window->display->xdisplay,
|
||||
window->xwindow,
|
||||
@@ -174,6 +178,7 @@ meta_window_destroy_frame (MetaWindow *window)
|
||||
{
|
||||
MetaFrame *frame;
|
||||
MetaFrameBorders borders;
|
||||
MetaStackWindow stack_window;
|
||||
|
||||
if (window->frame == NULL)
|
||||
return;
|
||||
@@ -200,8 +205,10 @@ meta_window_destroy_frame (MetaWindow *window)
|
||||
"Incrementing unmaps_pending on %s for reparent back to root\n", window->desc);
|
||||
window->unmaps_pending += 1;
|
||||
}
|
||||
stack_window.any.type = META_WINDOW_CLIENT_TYPE_X11;
|
||||
stack_window.x11.xwindow = window->xwindow;
|
||||
meta_stack_tracker_record_add (window->screen->stack_tracker,
|
||||
window->xwindow,
|
||||
&stack_window,
|
||||
XNextRequest (window->display->xdisplay));
|
||||
XReparentWindow (window->display->xdisplay,
|
||||
window->xwindow,
|
||||
|
||||
@@ -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 ************************/
|
||||
|
||||
@@ -55,8 +55,10 @@
|
||||
#include "session.h"
|
||||
#include <meta/prefs.h>
|
||||
#include <meta/compositor.h>
|
||||
#include "meta-wayland-private.h"
|
||||
|
||||
#include <glib-object.h>
|
||||
#include <glib-unix.h>
|
||||
#include <gdk/gdkx.h>
|
||||
|
||||
#include <stdlib.h>
|
||||
@@ -346,28 +348,17 @@ meta_finalize (void)
|
||||
if (display)
|
||||
meta_display_close (display,
|
||||
CurrentTime); /* I doubt correct timestamps matter here */
|
||||
}
|
||||
|
||||
static int sigterm_pipe_fds[2] = { -1, -1 };
|
||||
|
||||
static void
|
||||
sigterm_handler (int signum)
|
||||
{
|
||||
if (sigterm_pipe_fds[1] >= 0)
|
||||
{
|
||||
int G_GNUC_UNUSED dummy;
|
||||
|
||||
dummy = write (sigterm_pipe_fds[1], "", 1);
|
||||
close (sigterm_pipe_fds[1]);
|
||||
sigterm_pipe_fds[1] = -1;
|
||||
}
|
||||
if (meta_is_wayland_compositor ())
|
||||
meta_wayland_finalize ();
|
||||
}
|
||||
|
||||
static gboolean
|
||||
on_sigterm (void)
|
||||
on_sigterm (gpointer user_data)
|
||||
{
|
||||
meta_quit (META_EXIT_SUCCESS);
|
||||
return FALSE;
|
||||
meta_quit (EXIT_SUCCESS);
|
||||
|
||||
return G_SOURCE_REMOVE;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -381,7 +372,6 @@ meta_init (void)
|
||||
{
|
||||
struct sigaction act;
|
||||
sigset_t empty_mask;
|
||||
GIOChannel *channel;
|
||||
|
||||
sigemptyset (&empty_mask);
|
||||
act.sa_handler = SIG_IGN;
|
||||
@@ -396,20 +386,7 @@ meta_init (void)
|
||||
g_strerror (errno));
|
||||
#endif
|
||||
|
||||
if (pipe (sigterm_pipe_fds) != 0)
|
||||
g_printerr ("Failed to create SIGTERM pipe: %s\n",
|
||||
g_strerror (errno));
|
||||
|
||||
channel = g_io_channel_unix_new (sigterm_pipe_fds[0]);
|
||||
g_io_channel_set_flags (channel, G_IO_FLAG_NONBLOCK, NULL);
|
||||
g_io_add_watch (channel, G_IO_IN, (GIOFunc) on_sigterm, NULL);
|
||||
g_io_channel_set_close_on_unref (channel, TRUE);
|
||||
g_io_channel_unref (channel);
|
||||
|
||||
act.sa_handler = &sigterm_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);
|
||||
@@ -427,9 +404,16 @@ meta_init (void)
|
||||
g_irepository_prepend_search_path (MUTTER_PKGLIBDIR);
|
||||
#endif
|
||||
|
||||
meta_set_syncing (opt_sync || (g_getenv ("MUTTER_SYNC") != NULL));
|
||||
if (meta_is_wayland_compositor ())
|
||||
{
|
||||
/* NB: When running as a hybrid wayland compositor we run our own headless X
|
||||
* server so the user can't control the X display to connect too. */
|
||||
meta_wayland_init ();
|
||||
}
|
||||
else
|
||||
meta_select_display (opt_display_name);
|
||||
|
||||
meta_select_display (opt_display_name);
|
||||
meta_set_syncing (opt_sync || (g_getenv ("MUTTER_SYNC") != NULL));
|
||||
|
||||
if (opt_replace_wm)
|
||||
meta_set_replace_current_wm (TRUE);
|
||||
@@ -441,10 +425,17 @@ meta_init (void)
|
||||
|
||||
meta_ui_init ();
|
||||
|
||||
/*
|
||||
* Clutter can only be initialized after the UI.
|
||||
*/
|
||||
meta_clutter_init ();
|
||||
/* If we are running with wayland then we don't wait until we have
|
||||
* an X connection before initializing clutter we instead initialize
|
||||
* it earlier since we need to initialize the GL driver so the driver
|
||||
* can register any needed wayland extensions. */
|
||||
if (!meta_is_wayland_compositor ())
|
||||
{
|
||||
/*
|
||||
* Clutter can only be initialized after the UI.
|
||||
*/
|
||||
meta_clutter_init ();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
47
src/core/meta-cursor-tracker-private.h
Normal file
47
src/core/meta-cursor-tracker-private.h
Normal file
@@ -0,0 +1,47 @@
|
||||
/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
|
||||
|
||||
/*
|
||||
* Copyright 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.
|
||||
*
|
||||
* Author: Giovanni Campagna <gcampagn@redhat.com>
|
||||
*/
|
||||
|
||||
#ifndef META_CURSOR_TRACKER_PRIVATE_H
|
||||
#define META_CURSOR_TRACKER_PRIVATE_H
|
||||
|
||||
#include <meta/meta-cursor-tracker.h>
|
||||
#include <clutter/clutter.h>
|
||||
|
||||
gboolean meta_cursor_tracker_handle_xevent (MetaCursorTracker *tracker,
|
||||
XEvent *xevent);
|
||||
|
||||
void meta_cursor_tracker_set_root_cursor (MetaCursorTracker *tracker,
|
||||
MetaCursor cursor);
|
||||
void meta_cursor_tracker_revert_root (MetaCursorTracker *tracker);
|
||||
void meta_cursor_tracker_set_sprite (MetaCursorTracker *tracker,
|
||||
CoglTexture2D *texture,
|
||||
int hot_x,
|
||||
int hot_y);
|
||||
|
||||
void meta_cursor_tracker_update_position (MetaCursorTracker *tracker,
|
||||
int new_x,
|
||||
int new_y);
|
||||
void meta_cursor_tracker_paint (MetaCursorTracker *tracker);
|
||||
void meta_cursor_tracker_queue_redraw (MetaCursorTracker *tracker,
|
||||
ClutterActor *stage);
|
||||
#endif
|
||||
458
src/core/meta-cursor-tracker.c
Normal file
458
src/core/meta-cursor-tracker.c
Normal file
@@ -0,0 +1,458 @@
|
||||
/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
|
||||
|
||||
/*
|
||||
* Copyright 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.
|
||||
*
|
||||
* Author: Giovanni Campagna <gcampagn@redhat.com>
|
||||
*/
|
||||
|
||||
/**
|
||||
* SECTION:cursor-tracker
|
||||
* @title: MetaCursorTracker
|
||||
* @short_description: Mutter cursor tracking helper
|
||||
*/
|
||||
|
||||
#include <config.h>
|
||||
#include <meta/main.h>
|
||||
#include <meta/util.h>
|
||||
#include <meta/errors.h>
|
||||
|
||||
#include <cogl/cogl.h>
|
||||
#include <clutter/clutter.h>
|
||||
|
||||
#include <X11/extensions/Xfixes.h>
|
||||
|
||||
#include "meta-cursor-tracker-private.h"
|
||||
#include "screen-private.h"
|
||||
|
||||
#ifdef HAVE_WAYLAND
|
||||
#include "meta-wayland-private.h"
|
||||
#endif
|
||||
|
||||
#define META_WAYLAND_DEFAULT_CURSOR_HOTSPOT_X 7
|
||||
#define META_WAYLAND_DEFAULT_CURSOR_HOTSPOT_Y 4
|
||||
|
||||
struct _MetaCursorTracker {
|
||||
GObject parent_instance;
|
||||
|
||||
MetaScreen *screen;
|
||||
|
||||
gboolean is_showing;
|
||||
|
||||
CoglTexture2D *sprite;
|
||||
int hot_x, hot_y;
|
||||
|
||||
CoglTexture2D *root_cursor;
|
||||
int root_hot_x, root_hot_y;
|
||||
|
||||
CoglTexture2D *default_cursor;
|
||||
|
||||
int current_x, current_y;
|
||||
cairo_rectangle_int_t current_rect;
|
||||
cairo_rectangle_int_t previous_rect;
|
||||
gboolean previous_is_valid;
|
||||
|
||||
CoglPipeline *pipeline;
|
||||
};
|
||||
|
||||
struct _MetaCursorTrackerClass {
|
||||
GObjectClass parent_class;
|
||||
};
|
||||
|
||||
G_DEFINE_TYPE (MetaCursorTracker, meta_cursor_tracker, G_TYPE_OBJECT);
|
||||
|
||||
enum {
|
||||
CURSOR_CHANGED,
|
||||
LAST_SIGNAL
|
||||
};
|
||||
|
||||
static guint signals[LAST_SIGNAL];
|
||||
|
||||
static void
|
||||
meta_cursor_tracker_init (MetaCursorTracker *self)
|
||||
{
|
||||
/* (JS) Best (?) that can be assumed since XFixes doesn't provide a way of
|
||||
detecting if the system mouse cursor is showing or not.
|
||||
|
||||
On wayland we start with the cursor showing
|
||||
*/
|
||||
self->is_showing = TRUE;
|
||||
}
|
||||
|
||||
static void
|
||||
meta_cursor_tracker_finalize (GObject *object)
|
||||
{
|
||||
MetaCursorTracker *self = META_CURSOR_TRACKER (object);
|
||||
|
||||
if (self->sprite)
|
||||
cogl_object_unref (self->sprite);
|
||||
if (self->root_cursor)
|
||||
cogl_object_unref (self->root_cursor);
|
||||
if (self->default_cursor)
|
||||
cogl_object_unref (self->default_cursor);
|
||||
if (self->pipeline)
|
||||
cogl_object_unref (self->pipeline);
|
||||
|
||||
G_OBJECT_CLASS (meta_cursor_tracker_parent_class)->finalize (object);
|
||||
}
|
||||
|
||||
static void
|
||||
meta_cursor_tracker_class_init (MetaCursorTrackerClass *klass)
|
||||
{
|
||||
GObjectClass *object_class = G_OBJECT_CLASS (klass);
|
||||
|
||||
object_class->finalize = meta_cursor_tracker_finalize;
|
||||
|
||||
signals[CURSOR_CHANGED] = g_signal_new ("cursor-changed",
|
||||
G_TYPE_FROM_CLASS (klass),
|
||||
G_SIGNAL_RUN_LAST,
|
||||
0,
|
||||
NULL, NULL, NULL,
|
||||
G_TYPE_NONE, 0);
|
||||
}
|
||||
|
||||
#ifdef HAVE_WAYLAND
|
||||
static MetaCursorTracker *
|
||||
make_wayland_cursor_tracker (MetaScreen *screen)
|
||||
{
|
||||
MetaWaylandCompositor *compositor;
|
||||
CoglContext *ctx;
|
||||
MetaCursorTracker *self = g_object_new (META_TYPE_CURSOR_TRACKER, NULL);
|
||||
self->screen = screen;
|
||||
|
||||
ctx = clutter_backend_get_cogl_context (clutter_get_default_backend ());
|
||||
self->pipeline = cogl_pipeline_new (ctx);
|
||||
|
||||
compositor = meta_wayland_compositor_get_default ();
|
||||
compositor->seat->cursor_tracker = self;
|
||||
|
||||
return self;
|
||||
}
|
||||
#endif
|
||||
|
||||
static MetaCursorTracker *
|
||||
make_x11_cursor_tracker (MetaScreen *screen)
|
||||
{
|
||||
MetaCursorTracker *self = g_object_new (META_TYPE_CURSOR_TRACKER, NULL);
|
||||
self->screen = screen;
|
||||
|
||||
XFixesSelectCursorInput (screen->display->xdisplay,
|
||||
screen->xroot,
|
||||
XFixesDisplayCursorNotifyMask);
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
/**
|
||||
* meta_cursor_tracker_get_for_screen:
|
||||
* @screen: the #MetaScreen
|
||||
*
|
||||
* Retrieves the cursor tracker object for @screen.
|
||||
*
|
||||
* Returns: (transfer none):
|
||||
*/
|
||||
MetaCursorTracker *
|
||||
meta_cursor_tracker_get_for_screen (MetaScreen *screen)
|
||||
{
|
||||
MetaCursorTracker *self;
|
||||
|
||||
if (screen->cursor_tracker)
|
||||
return screen->cursor_tracker;
|
||||
|
||||
#ifdef HAVE_WAYLAND
|
||||
if (meta_is_wayland_compositor ())
|
||||
self = make_wayland_cursor_tracker (screen);
|
||||
else
|
||||
#endif
|
||||
self = make_x11_cursor_tracker (screen);
|
||||
|
||||
screen->cursor_tracker = self;
|
||||
return self;
|
||||
}
|
||||
|
||||
gboolean
|
||||
meta_cursor_tracker_handle_xevent (MetaCursorTracker *tracker,
|
||||
XEvent *xevent)
|
||||
{
|
||||
XFixesCursorNotifyEvent *notify_event;
|
||||
|
||||
if (meta_is_wayland_compositor ())
|
||||
return FALSE;
|
||||
|
||||
if (xevent->xany.type != tracker->screen->display->xfixes_event_base + XFixesCursorNotify)
|
||||
return FALSE;
|
||||
|
||||
notify_event = (XFixesCursorNotifyEvent *)xevent;
|
||||
if (notify_event->subtype != XFixesDisplayCursorNotify)
|
||||
return FALSE;
|
||||
|
||||
g_clear_pointer (&tracker->sprite, cogl_object_unref);
|
||||
g_signal_emit (tracker, signals[CURSOR_CHANGED], 0);
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static void
|
||||
ensure_xfixes_cursor (MetaCursorTracker *tracker)
|
||||
{
|
||||
XFixesCursorImage *cursor_image;
|
||||
CoglTexture2D *sprite;
|
||||
guint8 *cursor_data;
|
||||
gboolean free_cursor_data;
|
||||
CoglContext *ctx;
|
||||
|
||||
if (tracker->sprite)
|
||||
return;
|
||||
|
||||
cursor_image = XFixesGetCursorImage (tracker->screen->display->xdisplay);
|
||||
if (!cursor_image)
|
||||
return;
|
||||
|
||||
/* Like all X APIs, XFixesGetCursorImage() returns arrays of 32-bit
|
||||
* quantities as arrays of long; we need to convert on 64 bit */
|
||||
if (sizeof(long) == 4)
|
||||
{
|
||||
cursor_data = (guint8 *)cursor_image->pixels;
|
||||
free_cursor_data = FALSE;
|
||||
}
|
||||
else
|
||||
{
|
||||
int i, j;
|
||||
guint32 *cursor_words;
|
||||
gulong *p;
|
||||
guint32 *q;
|
||||
|
||||
cursor_words = g_new (guint32, cursor_image->width * cursor_image->height);
|
||||
cursor_data = (guint8 *)cursor_words;
|
||||
|
||||
p = cursor_image->pixels;
|
||||
q = cursor_words;
|
||||
for (j = 0; j < cursor_image->height; j++)
|
||||
for (i = 0; i < cursor_image->width; i++)
|
||||
*(q++) = *(p++);
|
||||
|
||||
free_cursor_data = TRUE;
|
||||
}
|
||||
|
||||
ctx = clutter_backend_get_cogl_context (clutter_get_default_backend ());
|
||||
sprite = cogl_texture_2d_new_from_data (ctx,
|
||||
cursor_image->width,
|
||||
cursor_image->height,
|
||||
CLUTTER_CAIRO_FORMAT_ARGB32,
|
||||
COGL_PIXEL_FORMAT_ANY,
|
||||
cursor_image->width * 4, /* stride */
|
||||
cursor_data,
|
||||
NULL);
|
||||
|
||||
if (free_cursor_data)
|
||||
g_free (cursor_data);
|
||||
|
||||
if (sprite != NULL)
|
||||
{
|
||||
tracker->sprite = sprite;
|
||||
tracker->hot_x = cursor_image->xhot;
|
||||
tracker->hot_y = cursor_image->yhot;
|
||||
}
|
||||
XFree (cursor_image);
|
||||
}
|
||||
|
||||
/**
|
||||
* meta_cursor_tracker_get_sprite:
|
||||
*
|
||||
* Returns: (transfer none):
|
||||
*/
|
||||
CoglTexture *
|
||||
meta_cursor_tracker_get_sprite (MetaCursorTracker *tracker)
|
||||
{
|
||||
g_return_val_if_fail (META_IS_CURSOR_TRACKER (tracker), NULL);
|
||||
|
||||
if (!meta_is_wayland_compositor ())
|
||||
ensure_xfixes_cursor (tracker);
|
||||
|
||||
return COGL_TEXTURE (tracker->sprite);
|
||||
}
|
||||
|
||||
/**
|
||||
* meta_cursor_tracker_get_hot:
|
||||
* @tracker:
|
||||
* @x: (out):
|
||||
* @y: (out):
|
||||
*
|
||||
*/
|
||||
void
|
||||
meta_cursor_tracker_get_hot (MetaCursorTracker *tracker,
|
||||
int *x,
|
||||
int *y)
|
||||
{
|
||||
g_return_if_fail (META_IS_CURSOR_TRACKER (tracker));
|
||||
|
||||
if (!meta_is_wayland_compositor ())
|
||||
ensure_xfixes_cursor (tracker);
|
||||
|
||||
if (x)
|
||||
*x = tracker->hot_x;
|
||||
if (y)
|
||||
*y = tracker->hot_y;
|
||||
}
|
||||
|
||||
static void
|
||||
ensure_wayland_cursor (MetaCursorTracker *tracker)
|
||||
{
|
||||
CoglBitmap *bitmap;
|
||||
char *filename;
|
||||
|
||||
if (tracker->default_cursor)
|
||||
return;
|
||||
|
||||
filename = g_build_filename (MUTTER_PKGDATADIR,
|
||||
"cursors/left_ptr.png",
|
||||
NULL);
|
||||
|
||||
bitmap = cogl_bitmap_new_from_file (filename, NULL);
|
||||
tracker->default_cursor = cogl_texture_2d_new_from_bitmap (bitmap,
|
||||
COGL_PIXEL_FORMAT_ANY,
|
||||
NULL);
|
||||
|
||||
cogl_object_unref (bitmap);
|
||||
g_free (filename);
|
||||
}
|
||||
|
||||
void
|
||||
meta_cursor_tracker_set_root_cursor (MetaCursorTracker *tracker,
|
||||
MetaCursor cursor)
|
||||
{
|
||||
Cursor xcursor;
|
||||
MetaDisplay *display = tracker->screen->display;
|
||||
|
||||
/* First create a cursor for X11 applications that don't specify their own */
|
||||
xcursor = meta_display_create_x_cursor (display, cursor);
|
||||
|
||||
XDefineCursor (display->xdisplay, tracker->screen->xroot, xcursor);
|
||||
XFlush (display->xdisplay);
|
||||
XFreeCursor (display->xdisplay, xcursor);
|
||||
|
||||
/* Now update the real root cursor */
|
||||
if (meta_is_wayland_compositor ())
|
||||
{
|
||||
/* FIXME! We need to load all the other cursors too */
|
||||
ensure_wayland_cursor (tracker);
|
||||
|
||||
g_clear_pointer (&tracker->root_cursor, cogl_object_unref);
|
||||
tracker->root_cursor = cogl_object_ref (tracker->default_cursor);
|
||||
tracker->root_hot_x = META_WAYLAND_DEFAULT_CURSOR_HOTSPOT_X;
|
||||
tracker->root_hot_y = META_WAYLAND_DEFAULT_CURSOR_HOTSPOT_Y;
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
meta_cursor_tracker_revert_root (MetaCursorTracker *tracker)
|
||||
{
|
||||
meta_cursor_tracker_set_sprite (tracker,
|
||||
tracker->root_cursor,
|
||||
tracker->root_hot_x,
|
||||
tracker->root_hot_y);
|
||||
}
|
||||
|
||||
void
|
||||
meta_cursor_tracker_set_sprite (MetaCursorTracker *tracker,
|
||||
CoglTexture2D *sprite,
|
||||
int hot_x,
|
||||
int hot_y)
|
||||
{
|
||||
g_assert (meta_is_wayland_compositor ());
|
||||
|
||||
g_clear_pointer (&tracker->sprite, cogl_object_unref);
|
||||
|
||||
if (sprite)
|
||||
{
|
||||
tracker->sprite = cogl_object_ref (sprite);
|
||||
tracker->hot_x = hot_x;
|
||||
tracker->hot_y = hot_y;
|
||||
cogl_pipeline_set_layer_texture (tracker->pipeline, 0, COGL_TEXTURE (tracker->sprite));
|
||||
}
|
||||
else
|
||||
cogl_pipeline_set_layer_texture (tracker->pipeline, 0, NULL);
|
||||
|
||||
g_signal_emit (tracker, signals[CURSOR_CHANGED], 0);
|
||||
|
||||
meta_cursor_tracker_update_position (tracker, tracker->current_x, tracker->current_y);
|
||||
}
|
||||
|
||||
void
|
||||
meta_cursor_tracker_update_position (MetaCursorTracker *tracker,
|
||||
int new_x,
|
||||
int new_y)
|
||||
{
|
||||
g_assert (meta_is_wayland_compositor ());
|
||||
|
||||
tracker->current_x = new_x;
|
||||
tracker->current_y = new_y;
|
||||
tracker->current_rect.x = tracker->current_x - tracker->hot_x;
|
||||
tracker->current_rect.y = tracker->current_y - tracker->hot_y;
|
||||
|
||||
if (tracker->sprite)
|
||||
{
|
||||
tracker->current_rect.width = cogl_texture_get_width (COGL_TEXTURE (tracker->sprite));
|
||||
tracker->current_rect.height = cogl_texture_get_height (COGL_TEXTURE (tracker->sprite));
|
||||
}
|
||||
else
|
||||
{
|
||||
tracker->current_rect.width = 0;
|
||||
tracker->current_rect.height = 0;
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
meta_cursor_tracker_paint (MetaCursorTracker *tracker)
|
||||
{
|
||||
g_assert (meta_is_wayland_compositor ());
|
||||
|
||||
if (tracker->sprite == NULL)
|
||||
return;
|
||||
|
||||
/* FIXME: try to use a DRM cursor when possible */
|
||||
cogl_framebuffer_draw_rectangle (cogl_get_draw_framebuffer (),
|
||||
tracker->pipeline,
|
||||
tracker->current_rect.x,
|
||||
tracker->current_rect.y,
|
||||
tracker->current_rect.x +
|
||||
tracker->current_rect.width,
|
||||
tracker->current_rect.y +
|
||||
tracker->current_rect.height);
|
||||
|
||||
tracker->previous_rect = tracker->current_rect;
|
||||
tracker->previous_is_valid = TRUE;
|
||||
}
|
||||
|
||||
void
|
||||
meta_cursor_tracker_queue_redraw (MetaCursorTracker *tracker,
|
||||
ClutterActor *stage)
|
||||
{
|
||||
g_assert (meta_is_wayland_compositor ());
|
||||
|
||||
if (tracker->previous_is_valid)
|
||||
{
|
||||
clutter_actor_queue_redraw_with_clip (stage, &tracker->previous_rect);
|
||||
tracker->previous_is_valid = FALSE;
|
||||
}
|
||||
|
||||
if (tracker->sprite == NULL)
|
||||
return;
|
||||
|
||||
clutter_actor_queue_redraw_with_clip (stage, &tracker->current_rect);
|
||||
}
|
||||
31
src/core/meta-idle-monitor-private.h
Normal file
31
src/core/meta-idle-monitor-private.h
Normal file
@@ -0,0 +1,31 @@
|
||||
/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
|
||||
|
||||
/*
|
||||
* Copyright 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.
|
||||
*
|
||||
* Adapted from gnome-session/gnome-session/gs-idle-monitor.c and
|
||||
* from gnome-desktop/libgnome-desktop/gnome-idle-monitor.c
|
||||
*/
|
||||
|
||||
#include <meta/meta-idle-monitor.h>
|
||||
|
||||
void meta_idle_monitor_handle_xevent_all (XEvent *xevent);
|
||||
|
||||
void meta_idle_monitor_reset_idletime (MetaIdleMonitor *monitor);
|
||||
|
||||
void meta_idle_monitor_init_dbus (void);
|
||||
965
src/core/meta-idle-monitor.c
Normal file
965
src/core/meta-idle-monitor.c
Normal file
@@ -0,0 +1,965 @@
|
||||
/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
|
||||
|
||||
/*
|
||||
* Copyright 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.
|
||||
*
|
||||
* Adapted from gnome-session/gnome-session/gs-idle-monitor.c and
|
||||
* from gnome-desktop/libgnome-desktop/gnome-idle-monitor.c
|
||||
*/
|
||||
|
||||
/**
|
||||
* SECTION:idle-monitor
|
||||
* @title: MetaIdleMonitor
|
||||
* @short_description: Mutter idle counter (similar to X's IDLETIME)
|
||||
*/
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#include <string.h>
|
||||
#include <clutter/clutter.h>
|
||||
#include <X11/Xlib.h>
|
||||
#include <X11/extensions/sync.h>
|
||||
|
||||
#include <meta/util.h>
|
||||
#include <meta/main.h>
|
||||
#include <meta/meta-idle-monitor.h>
|
||||
#include "display-private.h"
|
||||
#include "meta-idle-monitor-private.h"
|
||||
#include "meta-dbus-idle-monitor.h"
|
||||
|
||||
G_STATIC_ASSERT(sizeof(unsigned long) == sizeof(gpointer));
|
||||
|
||||
struct _MetaIdleMonitor
|
||||
{
|
||||
GObject parent_instance;
|
||||
|
||||
GHashTable *watches;
|
||||
GHashTable *alarms;
|
||||
int device_id;
|
||||
|
||||
/* X11 implementation */
|
||||
Display *display;
|
||||
int sync_event_base;
|
||||
XSyncCounter counter;
|
||||
XSyncAlarm user_active_alarm;
|
||||
|
||||
/* Wayland implementation */
|
||||
guint64 last_event_time;
|
||||
};
|
||||
|
||||
struct _MetaIdleMonitorClass
|
||||
{
|
||||
GObjectClass parent_class;
|
||||
};
|
||||
|
||||
typedef struct
|
||||
{
|
||||
MetaIdleMonitor *monitor;
|
||||
guint id;
|
||||
MetaIdleMonitorWatchFunc callback;
|
||||
gpointer user_data;
|
||||
GDestroyNotify notify;
|
||||
guint64 timeout_msec;
|
||||
|
||||
/* x11 */
|
||||
XSyncAlarm xalarm;
|
||||
|
||||
/* wayland */
|
||||
GSource *timeout_source;
|
||||
} MetaIdleMonitorWatch;
|
||||
|
||||
enum
|
||||
{
|
||||
PROP_0,
|
||||
PROP_DEVICE_ID,
|
||||
PROP_LAST,
|
||||
};
|
||||
|
||||
static GParamSpec *obj_props[PROP_LAST];
|
||||
|
||||
G_DEFINE_TYPE (MetaIdleMonitor, meta_idle_monitor, G_TYPE_OBJECT)
|
||||
|
||||
static MetaIdleMonitor *device_monitors[256];
|
||||
static int device_id_max;
|
||||
|
||||
static gint64
|
||||
_xsyncvalue_to_int64 (XSyncValue value)
|
||||
{
|
||||
return ((guint64) XSyncValueHigh32 (value)) << 32
|
||||
| (guint64) XSyncValueLow32 (value);
|
||||
}
|
||||
|
||||
#define GUINT64_TO_XSYNCVALUE(value, ret) XSyncIntsToValue (ret, (value) & 0xFFFFFFFF, ((guint64)(value)) >> 32)
|
||||
|
||||
static void
|
||||
fire_watch (MetaIdleMonitorWatch *watch)
|
||||
{
|
||||
MetaIdleMonitor *monitor;
|
||||
guint id;
|
||||
gboolean is_user_active_watch;
|
||||
|
||||
monitor = watch->monitor;
|
||||
g_object_ref (monitor);
|
||||
|
||||
id = watch->id;
|
||||
is_user_active_watch = (watch->timeout_msec == 0);
|
||||
|
||||
if (watch->callback)
|
||||
watch->callback (monitor, id, watch->user_data);
|
||||
|
||||
if (is_user_active_watch)
|
||||
meta_idle_monitor_remove_watch (monitor, id);
|
||||
|
||||
g_object_unref (monitor);
|
||||
}
|
||||
|
||||
static XSyncAlarm
|
||||
_xsync_alarm_set (MetaIdleMonitor *monitor,
|
||||
XSyncTestType test_type,
|
||||
guint64 interval,
|
||||
gboolean want_events)
|
||||
{
|
||||
XSyncAlarmAttributes attr;
|
||||
XSyncValue delta;
|
||||
guint flags;
|
||||
|
||||
flags = XSyncCACounter | XSyncCAValueType | XSyncCATestType |
|
||||
XSyncCAValue | XSyncCADelta | XSyncCAEvents;
|
||||
|
||||
XSyncIntToValue (&delta, 0);
|
||||
attr.trigger.counter = monitor->counter;
|
||||
attr.trigger.value_type = XSyncAbsolute;
|
||||
attr.delta = delta;
|
||||
attr.events = want_events;
|
||||
|
||||
GUINT64_TO_XSYNCVALUE (interval, &attr.trigger.wait_value);
|
||||
attr.trigger.test_type = test_type;
|
||||
return XSyncCreateAlarm (monitor->display, flags, &attr);
|
||||
}
|
||||
|
||||
static void
|
||||
ensure_alarm_rescheduled (Display *dpy,
|
||||
XSyncAlarm alarm)
|
||||
{
|
||||
XSyncAlarmAttributes attr;
|
||||
|
||||
/* Some versions of Xorg have an issue where alarms aren't
|
||||
* always rescheduled. Calling XSyncChangeAlarm, even
|
||||
* without any attributes, will reschedule the alarm. */
|
||||
XSyncChangeAlarm (dpy, alarm, 0, &attr);
|
||||
}
|
||||
|
||||
static void
|
||||
set_alarm_enabled (Display *dpy,
|
||||
XSyncAlarm alarm,
|
||||
gboolean enabled)
|
||||
{
|
||||
XSyncAlarmAttributes attr;
|
||||
attr.events = enabled;
|
||||
XSyncChangeAlarm (dpy, alarm, XSyncCAEvents, &attr);
|
||||
}
|
||||
|
||||
static void
|
||||
check_x11_watch (gpointer data,
|
||||
gpointer user_data)
|
||||
{
|
||||
MetaIdleMonitorWatch *watch = data;
|
||||
XSyncAlarm alarm = (XSyncAlarm) user_data;
|
||||
|
||||
if (watch->xalarm != alarm)
|
||||
return;
|
||||
|
||||
fire_watch (watch);
|
||||
}
|
||||
|
||||
static void
|
||||
meta_idle_monitor_handle_xevent (MetaIdleMonitor *monitor,
|
||||
XSyncAlarmNotifyEvent *alarm_event)
|
||||
{
|
||||
XSyncAlarm alarm;
|
||||
GList *watches;
|
||||
gboolean has_alarm;
|
||||
|
||||
if (alarm_event->state != XSyncAlarmActive)
|
||||
return;
|
||||
|
||||
alarm = alarm_event->alarm;
|
||||
|
||||
has_alarm = FALSE;
|
||||
|
||||
if (alarm == monitor->user_active_alarm)
|
||||
{
|
||||
set_alarm_enabled (monitor->display,
|
||||
alarm,
|
||||
FALSE);
|
||||
has_alarm = TRUE;
|
||||
}
|
||||
else if (g_hash_table_contains (monitor->alarms, (gpointer) alarm))
|
||||
{
|
||||
ensure_alarm_rescheduled (monitor->display,
|
||||
alarm);
|
||||
has_alarm = TRUE;
|
||||
}
|
||||
|
||||
if (has_alarm)
|
||||
{
|
||||
watches = g_hash_table_get_values (monitor->watches);
|
||||
|
||||
g_list_foreach (watches, check_x11_watch, (gpointer) alarm);
|
||||
g_list_free (watches);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
meta_idle_monitor_handle_xevent_all (XEvent *xevent)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i <= device_id_max; i++)
|
||||
if (device_monitors[i])
|
||||
meta_idle_monitor_handle_xevent (device_monitors[i], (XSyncAlarmNotifyEvent*)xevent);
|
||||
}
|
||||
|
||||
static char *
|
||||
counter_name_for_device (int device_id)
|
||||
{
|
||||
if (device_id > 0)
|
||||
return g_strdup_printf ("DEVICEIDLETIME %d", device_id);
|
||||
|
||||
return g_strdup ("IDLETIME");
|
||||
}
|
||||
|
||||
static XSyncCounter
|
||||
find_idletime_counter (MetaIdleMonitor *monitor)
|
||||
{
|
||||
int i;
|
||||
int ncounters;
|
||||
XSyncSystemCounter *counters;
|
||||
XSyncCounter counter = None;
|
||||
char *counter_name;
|
||||
|
||||
counter_name = counter_name_for_device (monitor->device_id);
|
||||
counters = XSyncListSystemCounters (monitor->display, &ncounters);
|
||||
for (i = 0; i < ncounters; i++)
|
||||
{
|
||||
if (counters[i].name != NULL && strcmp (counters[i].name, counter_name) == 0)
|
||||
{
|
||||
counter = counters[i].counter;
|
||||
break;
|
||||
}
|
||||
}
|
||||
XSyncFreeSystemCounterList (counters);
|
||||
g_free (counter_name);
|
||||
|
||||
return counter;
|
||||
}
|
||||
|
||||
static guint32
|
||||
get_next_watch_serial (void)
|
||||
{
|
||||
static guint32 serial = 0;
|
||||
g_atomic_int_inc (&serial);
|
||||
return serial;
|
||||
}
|
||||
|
||||
static void
|
||||
idle_monitor_watch_free (MetaIdleMonitorWatch *watch)
|
||||
{
|
||||
MetaIdleMonitor *monitor;
|
||||
|
||||
if (watch == NULL)
|
||||
return;
|
||||
|
||||
monitor = watch->monitor;
|
||||
|
||||
if (watch->notify != NULL)
|
||||
watch->notify (watch->user_data);
|
||||
|
||||
if (watch->xalarm != monitor->user_active_alarm &&
|
||||
watch->xalarm != None)
|
||||
{
|
||||
XSyncDestroyAlarm (monitor->display, watch->xalarm);
|
||||
g_hash_table_remove (monitor->alarms, (gpointer) watch->xalarm);
|
||||
}
|
||||
|
||||
if (watch->timeout_source != NULL)
|
||||
g_source_destroy (watch->timeout_source);
|
||||
|
||||
g_slice_free (MetaIdleMonitorWatch, watch);
|
||||
}
|
||||
|
||||
static void
|
||||
init_xsync (MetaIdleMonitor *monitor)
|
||||
{
|
||||
monitor->counter = find_idletime_counter (monitor);
|
||||
/* IDLETIME counter not found? */
|
||||
if (monitor->counter == None)
|
||||
{
|
||||
meta_warning ("IDLETIME counter not found\n");
|
||||
return;
|
||||
}
|
||||
|
||||
monitor->user_active_alarm = _xsync_alarm_set (monitor, XSyncNegativeTransition, 1, FALSE);
|
||||
}
|
||||
|
||||
static void
|
||||
meta_idle_monitor_dispose (GObject *object)
|
||||
{
|
||||
MetaIdleMonitor *monitor;
|
||||
|
||||
monitor = META_IDLE_MONITOR (object);
|
||||
|
||||
g_clear_pointer (&monitor->watches, g_hash_table_destroy);
|
||||
g_clear_pointer (&monitor->alarms, g_hash_table_destroy);
|
||||
|
||||
if (monitor->user_active_alarm != None)
|
||||
{
|
||||
XSyncDestroyAlarm (monitor->display, monitor->user_active_alarm);
|
||||
monitor->user_active_alarm = None;
|
||||
}
|
||||
|
||||
G_OBJECT_CLASS (meta_idle_monitor_parent_class)->dispose (object);
|
||||
}
|
||||
|
||||
static void
|
||||
meta_idle_monitor_get_property (GObject *object,
|
||||
guint prop_id,
|
||||
GValue *value,
|
||||
GParamSpec *pspec)
|
||||
{
|
||||
MetaIdleMonitor *monitor = META_IDLE_MONITOR (object);
|
||||
|
||||
switch (prop_id)
|
||||
{
|
||||
case PROP_DEVICE_ID:
|
||||
g_value_set_int (value, monitor->device_id);
|
||||
break;
|
||||
default:
|
||||
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
meta_idle_monitor_set_property (GObject *object,
|
||||
guint prop_id,
|
||||
const GValue *value,
|
||||
GParamSpec *pspec)
|
||||
{
|
||||
MetaIdleMonitor *monitor = META_IDLE_MONITOR (object);
|
||||
switch (prop_id)
|
||||
{
|
||||
case PROP_DEVICE_ID:
|
||||
monitor->device_id = g_value_get_int (value);
|
||||
break;
|
||||
default:
|
||||
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
meta_idle_monitor_constructed (GObject *object)
|
||||
{
|
||||
MetaIdleMonitor *monitor = META_IDLE_MONITOR (object);
|
||||
|
||||
if (!meta_is_wayland_compositor ())
|
||||
{
|
||||
monitor->display = meta_get_display ()->xdisplay;
|
||||
init_xsync (monitor);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
meta_idle_monitor_class_init (MetaIdleMonitorClass *klass)
|
||||
{
|
||||
GObjectClass *object_class = G_OBJECT_CLASS (klass);
|
||||
|
||||
object_class->dispose = meta_idle_monitor_dispose;
|
||||
object_class->constructed = meta_idle_monitor_constructed;
|
||||
object_class->get_property = meta_idle_monitor_get_property;
|
||||
object_class->set_property = meta_idle_monitor_set_property;
|
||||
|
||||
/**
|
||||
* MetaIdleMonitor:device_id:
|
||||
*
|
||||
* The device to listen to idletime on.
|
||||
*/
|
||||
obj_props[PROP_DEVICE_ID] =
|
||||
g_param_spec_int ("device-id",
|
||||
"Device ID",
|
||||
"The device to listen to idletime on",
|
||||
0, 255, 0,
|
||||
G_PARAM_STATIC_STRINGS | G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY);
|
||||
g_object_class_install_property (object_class, PROP_DEVICE_ID, obj_props[PROP_DEVICE_ID]);
|
||||
}
|
||||
|
||||
static void
|
||||
meta_idle_monitor_init (MetaIdleMonitor *monitor)
|
||||
{
|
||||
monitor->watches = g_hash_table_new_full (NULL,
|
||||
NULL,
|
||||
NULL,
|
||||
(GDestroyNotify)idle_monitor_watch_free);
|
||||
|
||||
monitor->alarms = g_hash_table_new (NULL, NULL);
|
||||
}
|
||||
|
||||
static void
|
||||
ensure_device_monitor (int device_id)
|
||||
{
|
||||
if (device_monitors[device_id])
|
||||
return;
|
||||
|
||||
device_monitors[device_id] = g_object_new (META_TYPE_IDLE_MONITOR, "device-id", device_id, NULL);
|
||||
device_id_max = MAX (device_id_max, device_id);
|
||||
}
|
||||
|
||||
/**
|
||||
* meta_idle_monitor_get_core:
|
||||
*
|
||||
* Returns: (transfer none): the #MetaIdleMonitor that tracks the server-global
|
||||
* idletime for all devices. To track device-specific idletime,
|
||||
* use meta_idle_monitor_get_for_device().
|
||||
*/
|
||||
MetaIdleMonitor *
|
||||
meta_idle_monitor_get_core (void)
|
||||
{
|
||||
ensure_device_monitor (0);
|
||||
return device_monitors[0];
|
||||
}
|
||||
|
||||
/**
|
||||
* meta_idle_monitor_get_for_device:
|
||||
* @device_id: the device to get the idle time for.
|
||||
*
|
||||
* Returns: (transfer none): a new #MetaIdleMonitor that tracks the
|
||||
* device-specific idletime for @device. To track server-global idletime
|
||||
* for all devices, use meta_idle_monitor_get_core().
|
||||
*/
|
||||
MetaIdleMonitor *
|
||||
meta_idle_monitor_get_for_device (int device_id)
|
||||
{
|
||||
g_return_val_if_fail (device_id > 0 && device_id < 256, NULL);
|
||||
|
||||
ensure_device_monitor (device_id);
|
||||
return device_monitors[device_id];
|
||||
}
|
||||
|
||||
static gboolean
|
||||
wayland_dispatch_timeout (GSource *source,
|
||||
GSourceFunc callback,
|
||||
gpointer user_data)
|
||||
{
|
||||
MetaIdleMonitorWatch *watch = user_data;
|
||||
|
||||
fire_watch (watch);
|
||||
g_source_set_ready_time (watch->timeout_source, -1);
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static GSourceFuncs wayland_source_funcs = {
|
||||
NULL, /* prepare */
|
||||
NULL, /* check */
|
||||
wayland_dispatch_timeout,
|
||||
NULL, /* finalize */
|
||||
};
|
||||
|
||||
static MetaIdleMonitorWatch *
|
||||
make_watch (MetaIdleMonitor *monitor,
|
||||
guint64 timeout_msec,
|
||||
MetaIdleMonitorWatchFunc callback,
|
||||
gpointer user_data,
|
||||
GDestroyNotify notify)
|
||||
{
|
||||
MetaIdleMonitorWatch *watch;
|
||||
|
||||
watch = g_slice_new0 (MetaIdleMonitorWatch);
|
||||
watch->monitor = monitor;
|
||||
watch->id = get_next_watch_serial ();
|
||||
watch->callback = callback;
|
||||
watch->user_data = user_data;
|
||||
watch->notify = notify;
|
||||
watch->timeout_msec = timeout_msec;
|
||||
|
||||
if (meta_is_wayland_compositor ())
|
||||
{
|
||||
if (timeout_msec != 0)
|
||||
{
|
||||
GSource *source = g_source_new (&wayland_source_funcs, sizeof (GSource));
|
||||
|
||||
g_source_set_callback (source, NULL, watch, NULL);
|
||||
g_source_set_ready_time (source, monitor->last_event_time + timeout_msec * 1000);
|
||||
g_source_attach (source, NULL);
|
||||
g_source_unref (source);
|
||||
|
||||
watch->timeout_source = source;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (timeout_msec != 0)
|
||||
{
|
||||
watch->xalarm = _xsync_alarm_set (monitor, XSyncPositiveTransition, timeout_msec, TRUE);
|
||||
|
||||
g_hash_table_add (monitor->alarms, (gpointer) watch->xalarm);
|
||||
}
|
||||
else
|
||||
{
|
||||
watch->xalarm = monitor->user_active_alarm;
|
||||
|
||||
set_alarm_enabled (monitor->display, monitor->user_active_alarm, TRUE);
|
||||
}
|
||||
}
|
||||
|
||||
g_hash_table_insert (monitor->watches,
|
||||
GUINT_TO_POINTER (watch->id),
|
||||
watch);
|
||||
return watch;
|
||||
}
|
||||
|
||||
/**
|
||||
* meta_idle_monitor_add_idle_watch:
|
||||
* @monitor: A #MetaIdleMonitor
|
||||
* @interval_msec: The idletime interval, in milliseconds
|
||||
* @callback: (allow-none): The callback to call when the user has
|
||||
* accumulated @interval_msec milliseconds of idle time.
|
||||
* @user_data: (allow-none): The user data to pass to the callback
|
||||
* @notify: A #GDestroyNotify
|
||||
*
|
||||
* Returns: a watch id
|
||||
*
|
||||
* Adds a watch for a specific idle time. The callback will be called
|
||||
* when the user has accumulated @interval_msec milliseconds of idle time.
|
||||
* This function will return an ID that can either be passed to
|
||||
* meta_idle_monitor_remove_watch(), or can be used to tell idle time
|
||||
* watches apart if you have more than one.
|
||||
*
|
||||
* Also note that this function will only care about positive transitions
|
||||
* (user's idle time exceeding a certain time). If you want to know about
|
||||
* when the user has become active, use
|
||||
* meta_idle_monitor_add_user_active_watch().
|
||||
*/
|
||||
guint
|
||||
meta_idle_monitor_add_idle_watch (MetaIdleMonitor *monitor,
|
||||
guint64 interval_msec,
|
||||
MetaIdleMonitorWatchFunc callback,
|
||||
gpointer user_data,
|
||||
GDestroyNotify notify)
|
||||
{
|
||||
MetaIdleMonitorWatch *watch;
|
||||
|
||||
g_return_val_if_fail (META_IS_IDLE_MONITOR (monitor), 0);
|
||||
g_return_val_if_fail (interval_msec > 0, 0);
|
||||
|
||||
watch = make_watch (monitor,
|
||||
interval_msec,
|
||||
callback,
|
||||
user_data,
|
||||
notify);
|
||||
|
||||
return watch->id;
|
||||
}
|
||||
|
||||
/**
|
||||
* meta_idle_monitor_add_user_active_watch:
|
||||
* @monitor: A #MetaIdleMonitor
|
||||
* @callback: (allow-none): The callback to call when the user is
|
||||
* active again.
|
||||
* @user_data: (allow-none): The user data to pass to the callback
|
||||
* @notify: A #GDestroyNotify
|
||||
*
|
||||
* Returns: a watch id
|
||||
*
|
||||
* Add a one-time watch to know when the user is active again.
|
||||
* Note that this watch is one-time and will de-activate after the
|
||||
* function is called, for efficiency purposes. It's most convenient
|
||||
* to call this when an idle watch, as added by
|
||||
* meta_idle_monitor_add_idle_watch(), has triggered.
|
||||
*/
|
||||
guint
|
||||
meta_idle_monitor_add_user_active_watch (MetaIdleMonitor *monitor,
|
||||
MetaIdleMonitorWatchFunc callback,
|
||||
gpointer user_data,
|
||||
GDestroyNotify notify)
|
||||
{
|
||||
MetaIdleMonitorWatch *watch;
|
||||
|
||||
g_return_val_if_fail (META_IS_IDLE_MONITOR (monitor), 0);
|
||||
|
||||
watch = make_watch (monitor,
|
||||
0,
|
||||
callback,
|
||||
user_data,
|
||||
notify);
|
||||
|
||||
return watch->id;
|
||||
}
|
||||
|
||||
/**
|
||||
* meta_idle_monitor_remove_watch:
|
||||
* @monitor: A #MetaIdleMonitor
|
||||
* @id: A watch ID
|
||||
*
|
||||
* Removes an idle time watcher, previously added by
|
||||
* meta_idle_monitor_add_idle_watch() or
|
||||
* meta_idle_monitor_add_user_active_watch().
|
||||
*/
|
||||
void
|
||||
meta_idle_monitor_remove_watch (MetaIdleMonitor *monitor,
|
||||
guint id)
|
||||
{
|
||||
g_return_if_fail (META_IS_IDLE_MONITOR (monitor));
|
||||
|
||||
g_hash_table_remove (monitor->watches,
|
||||
GUINT_TO_POINTER (id));
|
||||
}
|
||||
|
||||
/**
|
||||
* meta_idle_monitor_get_idletime:
|
||||
* @monitor: A #MetaIdleMonitor
|
||||
*
|
||||
* Returns: The current idle time, in milliseconds, or -1 for not supported
|
||||
*/
|
||||
gint64
|
||||
meta_idle_monitor_get_idletime (MetaIdleMonitor *monitor)
|
||||
{
|
||||
XSyncValue value;
|
||||
|
||||
if (meta_is_wayland_compositor ())
|
||||
{
|
||||
return (g_get_monotonic_time () - monitor->last_event_time) / 1000;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (!XSyncQueryCounter (monitor->display, monitor->counter, &value))
|
||||
return -1;
|
||||
|
||||
return _xsyncvalue_to_int64 (value);
|
||||
}
|
||||
}
|
||||
|
||||
typedef struct {
|
||||
MetaIdleMonitor *monitor;
|
||||
GList *fired_watches;
|
||||
} CheckWaylandClosure;
|
||||
|
||||
static gboolean
|
||||
check_wayland_watch (gpointer key,
|
||||
gpointer value,
|
||||
gpointer user_data)
|
||||
{
|
||||
MetaIdleMonitorWatch *watch = value;
|
||||
CheckWaylandClosure *closure = user_data;
|
||||
gboolean steal;
|
||||
|
||||
if (watch->timeout_msec == 0)
|
||||
{
|
||||
closure->fired_watches = g_list_prepend (closure->fired_watches, watch);
|
||||
steal = TRUE;
|
||||
}
|
||||
else
|
||||
{
|
||||
g_source_set_ready_time (watch->timeout_source,
|
||||
closure->monitor->last_event_time +
|
||||
watch->timeout_msec * 1000);
|
||||
steal = FALSE;
|
||||
}
|
||||
|
||||
return steal;
|
||||
}
|
||||
|
||||
static void
|
||||
fire_wayland_watch (gpointer watch,
|
||||
gpointer data)
|
||||
{
|
||||
fire_watch (watch);
|
||||
}
|
||||
|
||||
void
|
||||
meta_idle_monitor_reset_idletime (MetaIdleMonitor *monitor)
|
||||
{
|
||||
CheckWaylandClosure closure;
|
||||
|
||||
monitor->last_event_time = g_get_monotonic_time ();
|
||||
|
||||
closure.monitor = monitor;
|
||||
closure.fired_watches = NULL;
|
||||
g_hash_table_foreach_steal (monitor->watches, check_wayland_watch, &closure);
|
||||
|
||||
g_list_foreach (closure.fired_watches, fire_wayland_watch, NULL);
|
||||
g_list_free (closure.fired_watches);
|
||||
}
|
||||
|
||||
static gboolean
|
||||
handle_get_idletime (MetaDBusIdleMonitor *skeleton,
|
||||
GDBusMethodInvocation *invocation,
|
||||
MetaIdleMonitor *monitor)
|
||||
{
|
||||
guint64 idletime;
|
||||
|
||||
idletime = meta_idle_monitor_get_idletime (monitor);
|
||||
meta_dbus_idle_monitor_complete_get_idletime (skeleton, invocation, idletime);
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
typedef struct {
|
||||
MetaDBusIdleMonitor *dbus_monitor;
|
||||
MetaIdleMonitor *monitor;
|
||||
char *dbus_name;
|
||||
guint watch_id;
|
||||
guint name_watcher_id;
|
||||
} DBusWatch;
|
||||
|
||||
static void
|
||||
destroy_dbus_watch (gpointer data)
|
||||
{
|
||||
DBusWatch *watch = data;
|
||||
|
||||
g_object_unref (watch->dbus_monitor);
|
||||
g_object_unref (watch->monitor);
|
||||
g_free (watch->dbus_name);
|
||||
g_bus_unwatch_name (watch->name_watcher_id);
|
||||
|
||||
g_slice_free (DBusWatch, watch);
|
||||
}
|
||||
|
||||
static void
|
||||
dbus_idle_callback (MetaIdleMonitor *monitor,
|
||||
guint watch_id,
|
||||
gpointer user_data)
|
||||
{
|
||||
DBusWatch *watch = user_data;
|
||||
GDBusInterfaceSkeleton *skeleton = G_DBUS_INTERFACE_SKELETON (watch->dbus_monitor);
|
||||
|
||||
g_dbus_connection_emit_signal (g_dbus_interface_skeleton_get_connection (skeleton),
|
||||
watch->dbus_name,
|
||||
g_dbus_interface_skeleton_get_object_path (skeleton),
|
||||
"org.gnome.Mutter.IdleMonitor",
|
||||
"WatchFired",
|
||||
g_variant_new ("(u)", watch_id),
|
||||
NULL);
|
||||
}
|
||||
|
||||
static void
|
||||
name_vanished_callback (GDBusConnection *connection,
|
||||
const char *name,
|
||||
gpointer user_data)
|
||||
{
|
||||
DBusWatch *watch = user_data;
|
||||
|
||||
meta_idle_monitor_remove_watch (watch->monitor, watch->watch_id);
|
||||
}
|
||||
|
||||
static DBusWatch *
|
||||
make_dbus_watch (MetaDBusIdleMonitor *skeleton,
|
||||
GDBusMethodInvocation *invocation,
|
||||
MetaIdleMonitor *monitor)
|
||||
{
|
||||
DBusWatch *watch;
|
||||
|
||||
watch = g_slice_new (DBusWatch);
|
||||
watch->dbus_monitor = g_object_ref (skeleton);
|
||||
watch->monitor = g_object_ref (monitor);
|
||||
watch->dbus_name = g_strdup (g_dbus_method_invocation_get_sender (invocation));
|
||||
watch->name_watcher_id = g_bus_watch_name_on_connection (g_dbus_method_invocation_get_connection (invocation),
|
||||
watch->dbus_name,
|
||||
G_BUS_NAME_WATCHER_FLAGS_NONE,
|
||||
NULL, /* appeared */
|
||||
name_vanished_callback,
|
||||
watch, NULL);
|
||||
|
||||
return watch;
|
||||
}
|
||||
|
||||
static gboolean
|
||||
handle_add_idle_watch (MetaDBusIdleMonitor *skeleton,
|
||||
GDBusMethodInvocation *invocation,
|
||||
guint64 interval,
|
||||
MetaIdleMonitor *monitor)
|
||||
{
|
||||
DBusWatch *watch;
|
||||
|
||||
watch = make_dbus_watch (skeleton, invocation, monitor);
|
||||
watch->watch_id = meta_idle_monitor_add_idle_watch (monitor, interval,
|
||||
dbus_idle_callback, watch, destroy_dbus_watch);
|
||||
|
||||
meta_dbus_idle_monitor_complete_add_idle_watch (skeleton, invocation, watch->watch_id);
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static gboolean
|
||||
handle_add_user_active_watch (MetaDBusIdleMonitor *skeleton,
|
||||
GDBusMethodInvocation *invocation,
|
||||
MetaIdleMonitor *monitor)
|
||||
{
|
||||
DBusWatch *watch;
|
||||
|
||||
watch = make_dbus_watch (skeleton, invocation, monitor);
|
||||
watch->watch_id = meta_idle_monitor_add_user_active_watch (monitor,
|
||||
dbus_idle_callback, watch,
|
||||
destroy_dbus_watch);
|
||||
|
||||
meta_dbus_idle_monitor_complete_add_user_active_watch (skeleton, invocation, watch->watch_id);
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static gboolean
|
||||
handle_remove_watch (MetaDBusIdleMonitor *skeleton,
|
||||
GDBusMethodInvocation *invocation,
|
||||
guint id,
|
||||
MetaIdleMonitor *monitor)
|
||||
{
|
||||
meta_idle_monitor_remove_watch (monitor, id);
|
||||
meta_dbus_idle_monitor_complete_remove_watch (skeleton, invocation);
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static void
|
||||
create_monitor_skeleton (GDBusObjectManagerServer *manager,
|
||||
MetaIdleMonitor *monitor,
|
||||
const char *path)
|
||||
{
|
||||
MetaDBusIdleMonitor *skeleton;
|
||||
MetaDBusObjectSkeleton *object;
|
||||
|
||||
skeleton = meta_dbus_idle_monitor_skeleton_new ();
|
||||
g_signal_connect_object (skeleton, "handle-add-idle-watch",
|
||||
G_CALLBACK (handle_add_idle_watch), monitor, 0);
|
||||
g_signal_connect_object (skeleton, "handle-add-user-active-watch",
|
||||
G_CALLBACK (handle_add_user_active_watch), monitor, 0);
|
||||
g_signal_connect_object (skeleton, "handle-remove-watch",
|
||||
G_CALLBACK (handle_remove_watch), monitor, 0);
|
||||
g_signal_connect_object (skeleton, "handle-get-idletime",
|
||||
G_CALLBACK (handle_get_idletime), monitor, 0);
|
||||
|
||||
object = meta_dbus_object_skeleton_new (path);
|
||||
meta_dbus_object_skeleton_set_idle_monitor (object, skeleton);
|
||||
|
||||
g_dbus_object_manager_server_export (manager, G_DBUS_OBJECT_SKELETON (object));
|
||||
}
|
||||
|
||||
static void
|
||||
on_device_added (ClutterDeviceManager *device_manager,
|
||||
ClutterInputDevice *device,
|
||||
GDBusObjectManagerServer *manager)
|
||||
{
|
||||
|
||||
MetaIdleMonitor *monitor;
|
||||
int device_id;
|
||||
char *path;
|
||||
|
||||
device_id = clutter_input_device_get_device_id (device);
|
||||
monitor = meta_idle_monitor_get_for_device (device_id);
|
||||
path = g_strdup_printf ("/org/gnome/Mutter/IdleMonitor/Device%d", device_id);
|
||||
|
||||
create_monitor_skeleton (manager, monitor, path);
|
||||
g_free (path);
|
||||
}
|
||||
|
||||
static void
|
||||
on_device_removed (ClutterDeviceManager *device_manager,
|
||||
ClutterInputDevice *device,
|
||||
GDBusObjectManagerServer *manager)
|
||||
{
|
||||
int device_id;
|
||||
char *path;
|
||||
|
||||
device_id = clutter_input_device_get_device_id (device);
|
||||
path = g_strdup_printf ("/org/gnome/Mutter/IdleMonitor/Device%d", device_id);
|
||||
g_dbus_object_manager_server_unexport (manager, path);
|
||||
g_free (path);
|
||||
|
||||
g_clear_object (&device_monitors[device_id]);
|
||||
if (device_id == device_id_max)
|
||||
device_id_max--;
|
||||
}
|
||||
|
||||
static void
|
||||
on_bus_acquired (GDBusConnection *connection,
|
||||
const char *name,
|
||||
gpointer user_data)
|
||||
{
|
||||
GDBusObjectManagerServer *manager;
|
||||
ClutterDeviceManager *device_manager;
|
||||
MetaIdleMonitor *monitor;
|
||||
GSList *devices, *iter;
|
||||
char *path;
|
||||
|
||||
manager = g_dbus_object_manager_server_new ("/org/gnome/Mutter/IdleMonitor");
|
||||
|
||||
/* We never clear the core monitor, as that's supposed to cumulate idle times from
|
||||
all devices */
|
||||
monitor = meta_idle_monitor_get_core ();
|
||||
path = g_strdup ("/org/gnome/Mutter/IdleMonitor/Core");
|
||||
create_monitor_skeleton (manager, monitor, path);
|
||||
g_free (path);
|
||||
|
||||
device_manager = clutter_device_manager_get_default ();
|
||||
devices = clutter_device_manager_list_devices (device_manager);
|
||||
|
||||
for (iter = devices; iter; iter = iter->next)
|
||||
on_device_added (device_manager, iter->data, manager);
|
||||
|
||||
g_signal_connect_object (device_manager, "device-added",
|
||||
G_CALLBACK (on_device_added), manager, 0);
|
||||
g_signal_connect_object (device_manager, "device-removed",
|
||||
G_CALLBACK (on_device_removed), manager, 0);
|
||||
|
||||
g_dbus_object_manager_server_set_connection (manager, connection);
|
||||
}
|
||||
|
||||
static void
|
||||
on_name_acquired (GDBusConnection *connection,
|
||||
const char *name,
|
||||
gpointer user_data)
|
||||
{
|
||||
meta_verbose ("Acquired name %s\n", name);
|
||||
}
|
||||
|
||||
static void
|
||||
on_name_lost (GDBusConnection *connection,
|
||||
const char *name,
|
||||
gpointer user_data)
|
||||
{
|
||||
meta_verbose ("Lost or failed to acquire name %s\n", name);
|
||||
}
|
||||
|
||||
void
|
||||
meta_idle_monitor_init_dbus (void)
|
||||
{
|
||||
static int dbus_name_id;
|
||||
|
||||
if (dbus_name_id > 0)
|
||||
return;
|
||||
|
||||
dbus_name_id = g_bus_own_name (G_BUS_TYPE_SESSION,
|
||||
"org.gnome.Mutter.IdleMonitor",
|
||||
G_BUS_NAME_OWNER_FLAGS_ALLOW_REPLACEMENT |
|
||||
(meta_get_replace_current_wm () ?
|
||||
G_BUS_NAME_OWNER_FLAGS_REPLACE : 0),
|
||||
on_bus_acquired,
|
||||
on_name_acquired,
|
||||
on_name_lost,
|
||||
NULL, NULL);
|
||||
}
|
||||
|
||||
@@ -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 (¤t->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);
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -337,18 +337,23 @@ meta_monitor_manager_xrandr_read_current (MetaMonitorManager *manager)
|
||||
dpms_enabled)
|
||||
{
|
||||
switch (dpms_state)
|
||||
{
|
||||
case DPMSModeOn:
|
||||
manager->power_save_mode = META_POWER_SAVE_ON;
|
||||
case DPMSModeStandby:
|
||||
manager->power_save_mode = META_POWER_SAVE_STANDBY;
|
||||
case DPMSModeSuspend:
|
||||
manager->power_save_mode = META_POWER_SAVE_SUSPEND;
|
||||
case DPMSModeOff:
|
||||
manager->power_save_mode = META_POWER_SAVE_OFF;
|
||||
default:
|
||||
manager->power_save_mode = META_POWER_SAVE_UNKNOWN;
|
||||
}
|
||||
{
|
||||
case DPMSModeOn:
|
||||
manager->power_save_mode = META_POWER_SAVE_ON;
|
||||
break;
|
||||
case DPMSModeStandby:
|
||||
manager->power_save_mode = META_POWER_SAVE_STANDBY;
|
||||
break;
|
||||
case DPMSModeSuspend:
|
||||
manager->power_save_mode = META_POWER_SAVE_SUSPEND;
|
||||
break;
|
||||
case DPMSModeOff:
|
||||
manager->power_save_mode = META_POWER_SAVE_OFF;
|
||||
break;
|
||||
default:
|
||||
manager->power_save_mode = META_POWER_SAVE_UNKNOWN;
|
||||
break;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -527,7 +532,7 @@ meta_monitor_manager_xrandr_read_current (MetaMonitorManager *manager)
|
||||
*/
|
||||
for (j = 0; j < (unsigned)output->nclone; j++)
|
||||
{
|
||||
meta_output->possible_clones = GINT_TO_POINTER (output->clones[j]);
|
||||
meta_output->possible_clones[j] = GINT_TO_POINTER (output->clones[j]);
|
||||
}
|
||||
|
||||
meta_output->is_primary = ((XID)meta_output->output_id == primary_output);
|
||||
@@ -772,17 +777,51 @@ meta_monitor_manager_xrandr_apply_configuration (MetaMonitorManager *manager,
|
||||
{
|
||||
MetaMonitorMode *mode;
|
||||
XID *outputs;
|
||||
int j, n_outputs;
|
||||
unsigned int j, n_outputs;
|
||||
int width, height;
|
||||
Status ok;
|
||||
unsigned long old_controlled_mask;
|
||||
unsigned long new_controlled_mask;
|
||||
|
||||
mode = crtc_info->mode;
|
||||
|
||||
n_outputs = crtc_info->outputs->len;
|
||||
outputs = g_new (XID, n_outputs);
|
||||
|
||||
old_controlled_mask = 0;
|
||||
for (j = 0; j < manager->n_outputs; j++)
|
||||
{
|
||||
MetaOutput *output;
|
||||
|
||||
output = &manager->outputs[j];
|
||||
|
||||
if (output->crtc == crtc)
|
||||
old_controlled_mask |= 1UL << j;
|
||||
}
|
||||
|
||||
new_controlled_mask = 0;
|
||||
for (j = 0; j < n_outputs; j++)
|
||||
outputs[j] = ((MetaOutput**)crtc_info->outputs->pdata)[j]->output_id;
|
||||
{
|
||||
MetaOutput *output;
|
||||
|
||||
output = ((MetaOutput**)crtc_info->outputs->pdata)[j];
|
||||
|
||||
output->is_dirty = TRUE;
|
||||
output->crtc = crtc;
|
||||
new_controlled_mask |= 1UL << j;
|
||||
|
||||
outputs[j] = output->output_id;
|
||||
}
|
||||
|
||||
if (crtc->current_mode == mode &&
|
||||
crtc->rect.x == crtc_info->x &&
|
||||
crtc->rect.y == crtc_info->y &&
|
||||
crtc->transform == crtc_info->transform &&
|
||||
old_controlled_mask == new_controlled_mask)
|
||||
{
|
||||
/* No change */
|
||||
goto next;
|
||||
}
|
||||
|
||||
meta_error_trap_push (meta_get_display ());
|
||||
ok = XRRSetCrtcConfig (manager_xrandr->xdisplay,
|
||||
@@ -801,11 +840,9 @@ meta_monitor_manager_xrandr_apply_configuration (MetaMonitorManager *manager,
|
||||
(unsigned)(crtc->crtc_id), (unsigned)(mode->mode_id),
|
||||
mode->width, mode->height, (float)mode->refresh_rate,
|
||||
crtc_info->x, crtc_info->y, crtc_info->transform);
|
||||
continue;
|
||||
goto next;
|
||||
}
|
||||
|
||||
g_free (outputs);
|
||||
|
||||
if (meta_monitor_transform_is_rotated (crtc_info->transform))
|
||||
{
|
||||
width = mode->height;
|
||||
@@ -824,15 +861,8 @@ meta_monitor_manager_xrandr_apply_configuration (MetaMonitorManager *manager,
|
||||
crtc->current_mode = mode;
|
||||
crtc->transform = crtc_info->transform;
|
||||
|
||||
for (j = 0; j < n_outputs; j++)
|
||||
{
|
||||
MetaOutput *output;
|
||||
|
||||
output = ((MetaOutput**)crtc_info->outputs->pdata)[j];
|
||||
|
||||
output->is_dirty = TRUE;
|
||||
output->crtc = crtc;
|
||||
}
|
||||
next:
|
||||
g_free (outputs);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -944,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;
|
||||
}
|
||||
|
||||
|
||||
@@ -42,7 +42,6 @@
|
||||
#define ALL_WL_TRANSFORMS ((1 << (WL_OUTPUT_TRANSFORM_FLIPPED_270 + 1)) - 1)
|
||||
|
||||
enum {
|
||||
MONITORS_CHANGED,
|
||||
CONFIRM_DISPLAY_CHANGE,
|
||||
SIGNALS_LAST
|
||||
};
|
||||
@@ -60,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;
|
||||
@@ -131,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
|
||||
@@ -350,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 *
|
||||
@@ -475,6 +354,15 @@ make_logical_config (MetaMonitorManager *manager)
|
||||
manager->monitor_infos = (void*)g_array_free (monitor_infos, FALSE);
|
||||
}
|
||||
|
||||
static GType
|
||||
get_default_backend (void)
|
||||
{
|
||||
if (meta_is_wayland_compositor ())
|
||||
return META_TYPE_MONITOR_MANAGER; /* FIXME: KMS */
|
||||
else
|
||||
return META_TYPE_MONITOR_MANAGER_XRANDR;
|
||||
}
|
||||
|
||||
static MetaMonitorManager *
|
||||
meta_monitor_manager_new (void)
|
||||
{
|
||||
@@ -484,7 +372,7 @@ meta_monitor_manager_new (void)
|
||||
env = g_getenv ("META_DEBUG_MULTIMONITOR");
|
||||
|
||||
if (env == NULL)
|
||||
type = META_TYPE_MONITOR_MANAGER_XRANDR;
|
||||
type = get_default_backend ();
|
||||
else if (strcmp (env, "xrandr") == 0)
|
||||
type = META_TYPE_MONITOR_MANAGER_XRANDR;
|
||||
else
|
||||
@@ -528,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);
|
||||
}
|
||||
@@ -559,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;
|
||||
|
||||
@@ -584,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);
|
||||
@@ -660,14 +548,6 @@ meta_monitor_manager_class_init (MetaMonitorManagerClass *klass)
|
||||
klass->get_edid_file = get_edid_file_dummy;
|
||||
klass->read_edid = read_edid_dummy;
|
||||
|
||||
signals[MONITORS_CHANGED] =
|
||||
g_signal_new ("monitors-changed",
|
||||
G_TYPE_FROM_CLASS (object_class),
|
||||
G_SIGNAL_RUN_LAST,
|
||||
0,
|
||||
NULL, NULL, NULL,
|
||||
G_TYPE_NONE, 0);
|
||||
|
||||
signals[CONFIRM_DISPLAY_CHANGE] =
|
||||
g_signal_new ("confirm-display-change",
|
||||
G_TYPE_FROM_CLASS (object_class),
|
||||
@@ -813,6 +693,10 @@ meta_monitor_manager_handle_get_resources (MetaDBusDisplayConfig *skeleton,
|
||||
g_variant_new_string (output->product));
|
||||
g_variant_builder_add (&properties, "{sv}", "serial",
|
||||
g_variant_new_string (output->serial));
|
||||
g_variant_builder_add (&properties, "{sv}", "width-mm",
|
||||
g_variant_new_int32 (output->width_mm));
|
||||
g_variant_builder_add (&properties, "{sv}", "height-mm",
|
||||
g_variant_new_int32 (output->height_mm));
|
||||
g_variant_builder_add (&properties, "{sv}", "display-name",
|
||||
g_variant_new_take_string (make_display_name (manager, output)));
|
||||
g_variant_builder_add (&properties, "{sv}", "backlight",
|
||||
@@ -1233,7 +1117,7 @@ meta_monitor_manager_handle_change_backlight (MetaDBusDisplayConfig *skeleton,
|
||||
|
||||
META_MONITOR_MANAGER_GET_CLASS (manager)->change_backlight (manager, output, value);
|
||||
|
||||
meta_dbus_display_config_complete_change_backlight (skeleton, invocation);
|
||||
meta_dbus_display_config_complete_change_backlight (skeleton, invocation, output->backlight);
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
@@ -1481,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;
|
||||
|
||||
@@ -1493,7 +1377,7 @@ invalidate_logical_config (MetaMonitorManager *manager)
|
||||
|
||||
make_logical_config (manager);
|
||||
|
||||
g_signal_emit (manager, signals[MONITORS_CHANGED], 0);
|
||||
g_signal_emit_by_name (manager, "monitors-changed");
|
||||
|
||||
g_free (old_monitor_infos);
|
||||
}
|
||||
@@ -1503,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;
|
||||
}
|
||||
|
||||
|
||||
@@ -46,8 +46,15 @@ print_version (const gchar *option_name,
|
||||
}
|
||||
|
||||
static gchar *plugin = "default";
|
||||
static gboolean opt_nested = FALSE;
|
||||
|
||||
GOptionEntry mutter_options[] = {
|
||||
{
|
||||
"nested", 0, 0, G_OPTION_ARG_NONE,
|
||||
&opt_nested,
|
||||
N_("Run nested as an application for testing"),
|
||||
NULL,
|
||||
},
|
||||
{
|
||||
"version", 0, G_OPTION_FLAG_NO_ARG, G_OPTION_ARG_CALLBACK,
|
||||
print_version,
|
||||
@@ -77,6 +84,8 @@ main (int argc, char **argv)
|
||||
exit (1);
|
||||
}
|
||||
|
||||
meta_set_is_wayland_compositor (opt_nested);
|
||||
|
||||
if (plugin)
|
||||
meta_plugin_manager_load (plugin);
|
||||
|
||||
|
||||
@@ -83,6 +83,7 @@ struct _MetaScreen
|
||||
MetaStack *stack;
|
||||
MetaStackTracker *stack_tracker;
|
||||
|
||||
MetaCursorTracker *cursor_tracker;
|
||||
MetaCursor current_cursor;
|
||||
|
||||
Window flash_window;
|
||||
@@ -244,6 +245,11 @@ void meta_screen_workspace_switched (MetaScreen *screen,
|
||||
|
||||
void meta_screen_set_active_workspace_hint (MetaScreen *screen);
|
||||
|
||||
Window meta_screen_create_guard_window (Display *xdisplay, MetaScreen *screen);
|
||||
|
||||
gboolean meta_screen_handle_xevent (MetaScreen *screen,
|
||||
XEvent *xevent);
|
||||
|
||||
int meta_screen_xinerama_index_to_monitor_index (MetaScreen *screen,
|
||||
int index);
|
||||
int meta_screen_monitor_index_to_xinerama_index (MetaScreen *screen,
|
||||
|
||||
@@ -45,6 +45,8 @@
|
||||
#include <meta/compositor.h>
|
||||
#include "mutter-enum-types.h"
|
||||
#include "core.h"
|
||||
#include "meta-wayland-private.h"
|
||||
#include "meta-cursor-tracker-private.h"
|
||||
|
||||
#include <X11/extensions/Xinerama.h>
|
||||
|
||||
@@ -445,12 +447,13 @@ reload_monitor_infos (MetaScreen *screen)
|
||||
* should effectively be forwarded to events on the background actor,
|
||||
* providing that the scene graph is set up correctly.
|
||||
*/
|
||||
static Window
|
||||
create_guard_window (Display *xdisplay, MetaScreen *screen)
|
||||
Window
|
||||
meta_screen_create_guard_window (Display *xdisplay, MetaScreen *screen)
|
||||
{
|
||||
XSetWindowAttributes attributes;
|
||||
Window guard_window;
|
||||
gulong create_serial;
|
||||
MetaStackWindow stack_window;
|
||||
|
||||
attributes.event_mask = NoEventMask;
|
||||
attributes.override_redirect = True;
|
||||
@@ -483,12 +486,14 @@ create_guard_window (Display *xdisplay, MetaScreen *screen)
|
||||
XISelectEvents (xdisplay, guard_window, &mask, 1);
|
||||
}
|
||||
|
||||
stack_window.any.type = META_WINDOW_CLIENT_TYPE_X11;
|
||||
stack_window.x11.xwindow = guard_window;
|
||||
meta_stack_tracker_record_add (screen->stack_tracker,
|
||||
guard_window,
|
||||
&stack_window,
|
||||
create_serial);
|
||||
|
||||
meta_stack_tracker_record_lower (screen->stack_tracker,
|
||||
guard_window,
|
||||
&stack_window,
|
||||
XNextRequest (xdisplay));
|
||||
XLowerWindow (xdisplay, guard_window);
|
||||
XMapWindow (xdisplay, guard_window);
|
||||
@@ -669,8 +674,9 @@ meta_screen_new (MetaDisplay *display,
|
||||
screen->xscreen = ScreenOfDisplay (xdisplay, number);
|
||||
screen->xroot = xroot;
|
||||
screen->rect.x = screen->rect.y = 0;
|
||||
|
||||
meta_monitor_manager_initialize ();
|
||||
|
||||
if (!meta_is_wayland_compositor ())
|
||||
meta_monitor_manager_initialize ();
|
||||
|
||||
manager = meta_monitor_manager_get ();
|
||||
g_signal_connect (manager, "monitors-changed",
|
||||
@@ -705,7 +711,8 @@ meta_screen_new (MetaDisplay *display,
|
||||
screen->guard_window = None;
|
||||
|
||||
reload_monitor_infos (screen);
|
||||
|
||||
|
||||
meta_cursor_tracker_get_for_screen (screen);
|
||||
meta_screen_set_cursor (screen, META_CURSOR_DEFAULT);
|
||||
|
||||
/* Handle creating a no_focus_window for this screen */
|
||||
@@ -930,8 +937,8 @@ meta_screen_manage_all_windows (MetaScreen *screen)
|
||||
meta_display_grab (screen->display);
|
||||
|
||||
if (screen->guard_window == None)
|
||||
screen->guard_window = create_guard_window (screen->display->xdisplay,
|
||||
screen);
|
||||
screen->guard_window =
|
||||
meta_screen_create_guard_window (screen->display->xdisplay, screen);
|
||||
|
||||
windows = list_windows (screen);
|
||||
|
||||
@@ -1461,29 +1468,18 @@ void
|
||||
meta_screen_set_cursor (MetaScreen *screen,
|
||||
MetaCursor cursor)
|
||||
{
|
||||
Cursor xcursor;
|
||||
|
||||
if (cursor == screen->current_cursor)
|
||||
return;
|
||||
|
||||
screen->current_cursor = cursor;
|
||||
|
||||
xcursor = meta_display_create_x_cursor (screen->display, cursor);
|
||||
XDefineCursor (screen->display->xdisplay, screen->xroot, xcursor);
|
||||
XFlush (screen->display->xdisplay);
|
||||
XFreeCursor (screen->display->xdisplay, xcursor);
|
||||
meta_cursor_tracker_set_root_cursor (screen->cursor_tracker, cursor);
|
||||
}
|
||||
|
||||
void
|
||||
meta_screen_update_cursor (MetaScreen *screen)
|
||||
{
|
||||
Cursor xcursor;
|
||||
|
||||
xcursor = meta_display_create_x_cursor (screen->display,
|
||||
screen->current_cursor);
|
||||
XDefineCursor (screen->display->xdisplay, screen->xroot, xcursor);
|
||||
XFlush (screen->display->xdisplay);
|
||||
XFreeCursor (screen->display->xdisplay, xcursor);
|
||||
meta_cursor_tracker_set_root_cursor (screen->cursor_tracker,
|
||||
screen->current_cursor);
|
||||
}
|
||||
|
||||
void
|
||||
@@ -1746,12 +1742,15 @@ meta_screen_tile_preview_update_timeout (gpointer data)
|
||||
{
|
||||
Window xwindow;
|
||||
gulong create_serial;
|
||||
MetaStackWindow stack_window;
|
||||
|
||||
screen->tile_preview = meta_tile_preview_new (screen->number);
|
||||
xwindow = meta_tile_preview_get_xwindow (screen->tile_preview,
|
||||
&create_serial);
|
||||
stack_window.any.type = META_WINDOW_CLIENT_TYPE_X11;
|
||||
stack_window.x11.xwindow = xwindow;
|
||||
meta_stack_tracker_record_add (screen->stack_tracker,
|
||||
xwindow,
|
||||
&stack_window,
|
||||
create_serial);
|
||||
}
|
||||
|
||||
@@ -3688,3 +3687,13 @@ meta_screen_get_monitor_in_fullscreen (MetaScreen *screen,
|
||||
/* We use -1 as a flag to mean "not known yet" for notification purposes */
|
||||
return screen->monitor_infos[monitor].in_fullscreen == TRUE;
|
||||
}
|
||||
|
||||
gboolean
|
||||
meta_screen_handle_xevent (MetaScreen *screen,
|
||||
XEvent *xevent)
|
||||
{
|
||||
if (meta_cursor_tracker_handle_xevent (screen->cursor_tracker, xevent))
|
||||
return TRUE;
|
||||
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -37,36 +37,55 @@
|
||||
#define META_STACK_TRACKER_H
|
||||
|
||||
#include <meta/screen.h>
|
||||
#include <meta/window.h>
|
||||
|
||||
typedef struct _MetaStackTracker MetaStackTracker;
|
||||
|
||||
typedef union _MetaStackWindow
|
||||
{
|
||||
struct {
|
||||
MetaWindowClientType type;
|
||||
} any;
|
||||
struct {
|
||||
MetaWindowClientType type;
|
||||
Window xwindow;
|
||||
} x11;
|
||||
struct {
|
||||
MetaWindowClientType type;
|
||||
MetaWindow *meta_window;
|
||||
} wayland;
|
||||
} MetaStackWindow;
|
||||
|
||||
gboolean meta_stack_window_equal (const MetaStackWindow *a,
|
||||
const MetaStackWindow *b);
|
||||
|
||||
MetaStackTracker *meta_stack_tracker_new (MetaScreen *screen);
|
||||
void meta_stack_tracker_free (MetaStackTracker *tracker);
|
||||
|
||||
/* These functions are called when we make an X call that changes the
|
||||
* stacking order; this allows MetaStackTracker to predict stacking
|
||||
* order before it receives events back from the X server */
|
||||
void meta_stack_tracker_record_add (MetaStackTracker *tracker,
|
||||
Window window,
|
||||
gulong serial);
|
||||
void meta_stack_tracker_record_remove (MetaStackTracker *tracker,
|
||||
Window window,
|
||||
gulong serial);
|
||||
void meta_stack_tracker_record_restack_windows (MetaStackTracker *tracker,
|
||||
Window *windows,
|
||||
int n_windows,
|
||||
gulong serial);
|
||||
void meta_stack_tracker_record_raise_above (MetaStackTracker *tracker,
|
||||
Window window,
|
||||
Window sibling,
|
||||
gulong serial);
|
||||
void meta_stack_tracker_record_lower_below (MetaStackTracker *tracker,
|
||||
Window window,
|
||||
Window sibling,
|
||||
gulong serial);
|
||||
void meta_stack_tracker_record_lower (MetaStackTracker *tracker,
|
||||
Window window,
|
||||
gulong serial);
|
||||
void meta_stack_tracker_record_add (MetaStackTracker *tracker,
|
||||
const MetaStackWindow *window,
|
||||
gulong serial);
|
||||
void meta_stack_tracker_record_remove (MetaStackTracker *tracker,
|
||||
const MetaStackWindow *window,
|
||||
gulong serial);
|
||||
void meta_stack_tracker_record_restack_windows (MetaStackTracker *tracker,
|
||||
const MetaStackWindow *windows,
|
||||
int n_windows,
|
||||
gulong serial);
|
||||
void meta_stack_tracker_record_raise_above (MetaStackTracker *tracker,
|
||||
const MetaStackWindow *window,
|
||||
const MetaStackWindow *sibling,
|
||||
gulong serial);
|
||||
void meta_stack_tracker_record_lower_below (MetaStackTracker *tracker,
|
||||
const MetaStackWindow *window,
|
||||
const MetaStackWindow *sibling,
|
||||
gulong serial);
|
||||
void meta_stack_tracker_record_lower (MetaStackTracker *tracker,
|
||||
const MetaStackWindow *window,
|
||||
gulong serial);
|
||||
|
||||
/* These functions are used to update the stack when we get events
|
||||
* reflecting changes to the stacking order */
|
||||
@@ -79,9 +98,9 @@ void meta_stack_tracker_reparent_event (MetaStackTracker *tracker,
|
||||
void meta_stack_tracker_configure_event (MetaStackTracker *tracker,
|
||||
XConfigureEvent *event);
|
||||
|
||||
void meta_stack_tracker_get_stack (MetaStackTracker *tracker,
|
||||
Window **windows,
|
||||
int *n_windows);
|
||||
void meta_stack_tracker_get_stack (MetaStackTracker *tracker,
|
||||
MetaStackWindow **windows,
|
||||
int *n_entries);
|
||||
|
||||
void meta_stack_tracker_sync_stack (MetaStackTracker *tracker);
|
||||
void meta_stack_tracker_queue_sync_stack (MetaStackTracker *tracker);
|
||||
|
||||
534
src/core/stack.c
534
src/core/stack.c
@@ -52,7 +52,7 @@
|
||||
|
||||
#define WINDOW_IN_STACK(w) (w->stack_position >= 0)
|
||||
|
||||
static void stack_sync_to_server (MetaStack *stack);
|
||||
static void stack_sync_to_xserver (MetaStack *stack);
|
||||
static void meta_window_set_stack_position_no_sync (MetaWindow *window,
|
||||
int position);
|
||||
static void stack_do_window_deletions (MetaStack *stack);
|
||||
@@ -71,14 +71,14 @@ meta_stack_new (MetaScreen *screen)
|
||||
stack = g_new (MetaStack, 1);
|
||||
|
||||
stack->screen = screen;
|
||||
stack->windows = g_array_new (FALSE, FALSE, sizeof (Window));
|
||||
stack->xwindows = g_array_new (FALSE, FALSE, sizeof (Window));
|
||||
|
||||
stack->sorted = NULL;
|
||||
stack->added = NULL;
|
||||
stack->removed = NULL;
|
||||
|
||||
stack->freeze_count = 0;
|
||||
stack->last_root_children_stacked = NULL;
|
||||
stack->last_all_root_children_stacked = NULL;
|
||||
|
||||
stack->n_positions = 0;
|
||||
|
||||
@@ -89,17 +89,34 @@ meta_stack_new (MetaScreen *screen)
|
||||
return stack;
|
||||
}
|
||||
|
||||
static void
|
||||
free_last_all_root_children_stacked_cache (MetaStack *stack)
|
||||
{
|
||||
unsigned int i;
|
||||
|
||||
for (i = 0; i < stack->last_all_root_children_stacked->len; i++)
|
||||
{
|
||||
MetaStackWindow *window = &g_array_index (stack->last_all_root_children_stacked, MetaStackWindow, i);
|
||||
if (window->any.type == META_WINDOW_CLIENT_TYPE_WAYLAND)
|
||||
g_object_remove_weak_pointer (G_OBJECT (window->wayland.meta_window),
|
||||
(gpointer *)&window->wayland.meta_window);
|
||||
}
|
||||
|
||||
g_array_free (stack->last_all_root_children_stacked, TRUE);
|
||||
stack->last_all_root_children_stacked = NULL;
|
||||
}
|
||||
|
||||
void
|
||||
meta_stack_free (MetaStack *stack)
|
||||
{
|
||||
g_array_free (stack->windows, TRUE);
|
||||
g_array_free (stack->xwindows, TRUE);
|
||||
|
||||
g_list_free (stack->sorted);
|
||||
g_list_free (stack->added);
|
||||
g_list_free (stack->removed);
|
||||
|
||||
if (stack->last_root_children_stacked)
|
||||
g_array_free (stack->last_root_children_stacked, TRUE);
|
||||
if (stack->last_all_root_children_stacked)
|
||||
free_last_all_root_children_stacked_cache (stack);
|
||||
|
||||
g_free (stack);
|
||||
}
|
||||
@@ -121,7 +138,7 @@ meta_stack_add (MetaStack *stack,
|
||||
"Window %s has stack_position initialized to %d\n",
|
||||
window->desc, window->stack_position);
|
||||
|
||||
stack_sync_to_server (stack);
|
||||
stack_sync_to_xserver (stack);
|
||||
meta_stack_update_window_tile_matches (stack, window->screen->active_workspace);
|
||||
}
|
||||
|
||||
@@ -157,7 +174,7 @@ meta_stack_remove (MetaStack *stack,
|
||||
stack->removed = g_list_prepend (stack->removed,
|
||||
GUINT_TO_POINTER (window->frame->xwindow));
|
||||
|
||||
stack_sync_to_server (stack);
|
||||
stack_sync_to_xserver (stack);
|
||||
meta_stack_update_window_tile_matches (stack, window->screen->active_workspace);
|
||||
}
|
||||
|
||||
@@ -167,7 +184,7 @@ meta_stack_update_layer (MetaStack *stack,
|
||||
{
|
||||
stack->need_relayer = TRUE;
|
||||
|
||||
stack_sync_to_server (stack);
|
||||
stack_sync_to_xserver (stack);
|
||||
meta_stack_update_window_tile_matches (stack, window->screen->active_workspace);
|
||||
}
|
||||
|
||||
@@ -177,7 +194,7 @@ meta_stack_update_transient (MetaStack *stack,
|
||||
{
|
||||
stack->need_constrain = TRUE;
|
||||
|
||||
stack_sync_to_server (stack);
|
||||
stack_sync_to_xserver (stack);
|
||||
meta_stack_update_window_tile_matches (stack, window->screen->active_workspace);
|
||||
}
|
||||
|
||||
@@ -206,7 +223,7 @@ meta_stack_raise (MetaStack *stack,
|
||||
|
||||
meta_window_set_stack_position_no_sync (window, max_stack_position);
|
||||
|
||||
stack_sync_to_server (stack);
|
||||
stack_sync_to_xserver (stack);
|
||||
meta_stack_update_window_tile_matches (stack, window->screen->active_workspace);
|
||||
}
|
||||
|
||||
@@ -234,7 +251,7 @@ meta_stack_lower (MetaStack *stack,
|
||||
|
||||
meta_window_set_stack_position_no_sync (window, min_stack_position);
|
||||
|
||||
stack_sync_to_server (stack);
|
||||
stack_sync_to_xserver (stack);
|
||||
meta_stack_update_window_tile_matches (stack, window->screen->active_workspace);
|
||||
}
|
||||
|
||||
@@ -250,7 +267,7 @@ meta_stack_thaw (MetaStack *stack)
|
||||
g_return_if_fail (stack->freeze_count > 0);
|
||||
|
||||
stack->freeze_count -= 1;
|
||||
stack_sync_to_server (stack);
|
||||
stack_sync_to_xserver (stack);
|
||||
meta_stack_update_window_tile_matches (stack, NULL);
|
||||
}
|
||||
|
||||
@@ -829,7 +846,7 @@ stack_do_window_deletions (MetaStack *stack)
|
||||
/* We go from the end figuring removals are more
|
||||
* likely to be recent.
|
||||
*/
|
||||
i = stack->windows->len;
|
||||
i = stack->xwindows->len;
|
||||
while (i > 0)
|
||||
{
|
||||
--i;
|
||||
@@ -840,9 +857,9 @@ stack_do_window_deletions (MetaStack *stack)
|
||||
* both the window->xwindow and window->frame->xwindow
|
||||
* in the removal list.
|
||||
*/
|
||||
if (xwindow == g_array_index (stack->windows, Window, i))
|
||||
if (xwindow == g_array_index (stack->xwindows, Window, i))
|
||||
{
|
||||
g_array_remove_index (stack->windows, i);
|
||||
g_array_remove_index (stack->xwindows, i);
|
||||
goto next;
|
||||
}
|
||||
}
|
||||
@@ -871,10 +888,10 @@ stack_do_window_additions (MetaStack *stack)
|
||||
"Adding %d windows to sorted list\n",
|
||||
n_added);
|
||||
|
||||
old_size = stack->windows->len;
|
||||
g_array_set_size (stack->windows, old_size + n_added);
|
||||
old_size = stack->xwindows->len;
|
||||
g_array_set_size (stack->xwindows, old_size + n_added);
|
||||
|
||||
end = &g_array_index (stack->windows, Window, old_size);
|
||||
end = &g_array_index (stack->xwindows, Window, old_size);
|
||||
|
||||
/* stack->added has the most recent additions at the
|
||||
* front of the list, so we need to reverse it
|
||||
@@ -1029,6 +1046,102 @@ stack_ensure_sorted (MetaStack *stack)
|
||||
stack_do_resort (stack);
|
||||
}
|
||||
|
||||
static MetaStackWindow *
|
||||
find_top_most_managed_window (MetaScreen *screen,
|
||||
const MetaStackWindow *ignore)
|
||||
{
|
||||
MetaStackTracker *stack_tracker = screen->stack_tracker;
|
||||
MetaStackWindow *windows;
|
||||
int n_windows;
|
||||
int i;
|
||||
|
||||
meta_stack_tracker_get_stack (stack_tracker,
|
||||
&windows, &n_windows);
|
||||
|
||||
/* Children are in order from bottom to top. We want to
|
||||
* find the topmost managed child, then configure
|
||||
* our window to be above it.
|
||||
*/
|
||||
for (i = n_windows -1; i >= 0; i--)
|
||||
{
|
||||
MetaStackWindow *other_window = &windows[i];
|
||||
|
||||
if (other_window->any.type == ignore->any.type &&
|
||||
((other_window->any.type == META_WINDOW_CLIENT_TYPE_X11 &&
|
||||
other_window->x11.xwindow == ignore->x11.xwindow) ||
|
||||
other_window->wayland.meta_window == ignore->wayland.meta_window))
|
||||
{
|
||||
/* Do nothing. This means we're already the topmost managed
|
||||
* window, but it DOES NOT mean we are already just above
|
||||
* the topmost managed window. This is important because if
|
||||
* an override redirect window is up, and we map a new
|
||||
* managed window, the new window is probably above the old
|
||||
* popup by default, and we want to push it below that
|
||||
* popup. So keep looking for a sibling managed window
|
||||
* to be moved below.
|
||||
*/
|
||||
}
|
||||
else
|
||||
{
|
||||
if (other_window->any.type == META_WINDOW_CLIENT_TYPE_X11)
|
||||
{
|
||||
MetaWindow *other = meta_display_lookup_x_window (screen->display,
|
||||
other_window->x11.xwindow);
|
||||
|
||||
if (other != NULL && !other->override_redirect)
|
||||
return other_window;
|
||||
}
|
||||
else
|
||||
{
|
||||
/* All wayland windows are currently considered "managed"
|
||||
* TODO: consider wayland pop-up windows like override
|
||||
* redirect windows here. */
|
||||
return other_window;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* When moving an X window we sometimes need an X based sibling.
|
||||
*
|
||||
* If the given sibling is X based this function returns it back
|
||||
* otherwise it searches downwards looking for the nearest X window.
|
||||
*
|
||||
* If no X based sibling could be found return NULL. */
|
||||
static MetaStackWindow *
|
||||
find_x11_sibling_downwards (MetaScreen *screen,
|
||||
MetaStackWindow *sibling)
|
||||
{
|
||||
MetaStackTracker *stack_tracker = screen->stack_tracker;
|
||||
MetaStackWindow *windows;
|
||||
int n_windows;
|
||||
int i;
|
||||
|
||||
if (sibling->any.type == META_WINDOW_CLIENT_TYPE_X11)
|
||||
return sibling;
|
||||
|
||||
meta_stack_tracker_get_stack (stack_tracker,
|
||||
&windows, &n_windows);
|
||||
|
||||
/* NB: Children are in order from bottom to top and we
|
||||
* want to search downwards for the nearest X window.
|
||||
*/
|
||||
|
||||
for (i = n_windows - 1; i >= 0; i--)
|
||||
if (meta_stack_window_equal (&windows[i], sibling))
|
||||
break;
|
||||
|
||||
for (; i >= 0; i--)
|
||||
{
|
||||
if (windows[i].any.type == META_WINDOW_CLIENT_TYPE_X11)
|
||||
return &windows[i];
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/**
|
||||
* raise_window_relative_to_managed_windows:
|
||||
*
|
||||
@@ -1053,84 +1166,74 @@ stack_ensure_sorted (MetaStack *stack)
|
||||
*/
|
||||
static void
|
||||
raise_window_relative_to_managed_windows (MetaScreen *screen,
|
||||
Window xwindow)
|
||||
const MetaStackWindow *window)
|
||||
{
|
||||
gulong serial = 0;
|
||||
MetaStackWindow *sibling;
|
||||
|
||||
Window *children;
|
||||
int n_children;
|
||||
int i;
|
||||
|
||||
meta_stack_tracker_get_stack (screen->stack_tracker,
|
||||
&children, &n_children);
|
||||
|
||||
/* Children are in order from bottom to top. We want to
|
||||
* find the topmost managed child, then configure
|
||||
* our window to be above it.
|
||||
*/
|
||||
i = n_children - 1;
|
||||
while (i >= 0)
|
||||
sibling = find_top_most_managed_window (screen, window);
|
||||
if (!sibling)
|
||||
{
|
||||
if (children[i] == xwindow)
|
||||
if (window->any.type == META_WINDOW_CLIENT_TYPE_X11)
|
||||
{
|
||||
/* Do nothing. This means we're already the topmost managed
|
||||
* window, but it DOES NOT mean we are already just above
|
||||
* the topmost managed window. This is important because if
|
||||
* an override redirect window is up, and we map a new
|
||||
* managed window, the new window is probably above the old
|
||||
* popup by default, and we want to push it below that
|
||||
* popup. So keep looking for a sibling managed window
|
||||
* to be moved below.
|
||||
*/
|
||||
serial = XNextRequest (screen->display->xdisplay);
|
||||
meta_error_trap_push (screen->display);
|
||||
XLowerWindow (screen->display->xdisplay,
|
||||
window->x11.xwindow);
|
||||
meta_error_trap_pop (screen->display);
|
||||
}
|
||||
else
|
||||
{
|
||||
MetaWindow *other = meta_display_lookup_x_window (screen->display,
|
||||
children[i]);
|
||||
if (other != NULL && !other->override_redirect)
|
||||
{
|
||||
XWindowChanges changes;
|
||||
|
||||
/* children[i] is the topmost managed child */
|
||||
/* No sibling to use, just lower ourselves to the bottom
|
||||
* to be sure we're below any override redirect windows.
|
||||
*/
|
||||
meta_stack_tracker_record_lower (screen->stack_tracker,
|
||||
window,
|
||||
serial);
|
||||
return;
|
||||
}
|
||||
|
||||
/* window is the topmost managed child */
|
||||
meta_topic (META_DEBUG_STACK,
|
||||
"Moving 0x%lx above topmost managed child window 0x%lx\n",
|
||||
xwindow, children[i]);
|
||||
window->any.type == META_WINDOW_CLIENT_TYPE_X11 ? window->x11.xwindow: 0,
|
||||
sibling->any.type == META_WINDOW_CLIENT_TYPE_X11 ? sibling->x11.xwindow: 0);
|
||||
|
||||
changes.sibling = children[i];
|
||||
if (window->any.type == META_WINDOW_CLIENT_TYPE_X11)
|
||||
{
|
||||
XWindowChanges changes;
|
||||
MetaStackWindow *x11_sibling = find_x11_sibling_downwards (screen, sibling);
|
||||
serial = XNextRequest (screen->display->xdisplay);
|
||||
|
||||
if (x11_sibling)
|
||||
{
|
||||
changes.sibling = x11_sibling->x11.xwindow;
|
||||
changes.stack_mode = Above;
|
||||
|
||||
meta_error_trap_push (screen->display);
|
||||
meta_stack_tracker_record_raise_above (screen->stack_tracker,
|
||||
xwindow,
|
||||
children[i],
|
||||
XNextRequest (screen->display->xdisplay));
|
||||
XConfigureWindow (screen->display->xdisplay,
|
||||
xwindow,
|
||||
window->x11.xwindow,
|
||||
CWSibling | CWStackMode,
|
||||
&changes);
|
||||
meta_error_trap_pop (screen->display);
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
--i;
|
||||
}
|
||||
|
||||
if (i < 0)
|
||||
else
|
||||
{
|
||||
/* No sibling to use, just lower ourselves to the bottom
|
||||
* to be sure we're below any override redirect windows.
|
||||
*/
|
||||
meta_error_trap_push (screen->display);
|
||||
meta_stack_tracker_record_lower (screen->stack_tracker,
|
||||
xwindow,
|
||||
XNextRequest (screen->display->xdisplay));
|
||||
XLowerWindow (screen->display->xdisplay,
|
||||
xwindow);
|
||||
window->x11.xwindow);
|
||||
meta_error_trap_pop (screen->display);
|
||||
}
|
||||
}
|
||||
|
||||
meta_stack_tracker_record_raise_above (screen->stack_tracker,
|
||||
window,
|
||||
sibling,
|
||||
serial);
|
||||
}
|
||||
|
||||
/**
|
||||
* stack_sync_to_server:
|
||||
*
|
||||
@@ -1145,13 +1248,16 @@ raise_window_relative_to_managed_windows (MetaScreen *screen,
|
||||
* job of computing the minimal set of stacking requests needed.
|
||||
*/
|
||||
static void
|
||||
stack_sync_to_server (MetaStack *stack)
|
||||
stack_sync_to_xserver (MetaStack *stack)
|
||||
{
|
||||
GArray *stacked;
|
||||
GArray *root_children_stacked;
|
||||
GArray *x11_stacked;
|
||||
GArray *x11_root_children_stacked;
|
||||
GArray *all_root_children_stacked; /* wayland OR x11 */
|
||||
GList *tmp;
|
||||
GArray *all_hidden;
|
||||
GArray *x11_hidden;
|
||||
GArray *x11_hidden_stack_windows;
|
||||
int n_override_redirect = 0;
|
||||
MetaStackWindow guard_stack_window;
|
||||
|
||||
/* Bail out if frozen */
|
||||
if (stack->freeze_count > 0)
|
||||
@@ -1166,13 +1272,17 @@ stack_sync_to_server (MetaStack *stack)
|
||||
* _NET hints, and "root_children_stacked" is in top-to-bottom
|
||||
* order for XRestackWindows()
|
||||
*/
|
||||
stacked = g_array_new (FALSE, FALSE, sizeof (Window));
|
||||
root_children_stacked = g_array_new (FALSE, FALSE, sizeof (Window));
|
||||
all_hidden = g_array_new (FALSE, FALSE, sizeof (Window));
|
||||
x11_stacked = g_array_new (FALSE, FALSE, sizeof (Window));
|
||||
|
||||
all_root_children_stacked = g_array_new (FALSE, FALSE, sizeof (MetaStackWindow));
|
||||
x11_root_children_stacked = g_array_new (FALSE, FALSE, sizeof (Window));
|
||||
|
||||
x11_hidden_stack_windows = g_array_new (FALSE, FALSE, sizeof (MetaStackWindow));
|
||||
x11_hidden = g_array_new (FALSE, FALSE, sizeof (Window));
|
||||
|
||||
/* The screen guard window sits above all hidden windows and acts as
|
||||
* a barrier to input reaching these windows. */
|
||||
g_array_append_val (all_hidden, stack->screen->guard_window);
|
||||
g_array_append_val (x11_hidden, stack->screen->guard_window);
|
||||
|
||||
meta_topic (META_DEBUG_STACK, "Top to bottom: ");
|
||||
meta_push_no_msg_prefix ();
|
||||
@@ -1181,6 +1291,9 @@ stack_sync_to_server (MetaStack *stack)
|
||||
{
|
||||
MetaWindow *w = tmp->data;
|
||||
Window top_level_window;
|
||||
MetaStackWindow stack_window;
|
||||
|
||||
stack_window.any.type = w->client_type;
|
||||
|
||||
meta_topic (META_DEBUG_STACK, "%u:%d - %s ",
|
||||
w->layer, w->stack_position, w->desc);
|
||||
@@ -1189,60 +1302,93 @@ stack_sync_to_server (MetaStack *stack)
|
||||
if (w->override_redirect)
|
||||
n_override_redirect++;
|
||||
else
|
||||
g_array_prepend_val (stacked, w->xwindow);
|
||||
g_array_prepend_val (x11_stacked, w->xwindow);
|
||||
|
||||
if (w->frame)
|
||||
top_level_window = w->frame->xwindow;
|
||||
else
|
||||
top_level_window = w->xwindow;
|
||||
|
||||
if (w->client_type == META_WINDOW_CLIENT_TYPE_X11)
|
||||
stack_window.x11.xwindow = top_level_window;
|
||||
else
|
||||
stack_window.wayland.meta_window = w;
|
||||
|
||||
/* We don't restack hidden windows along with the rest, though they are
|
||||
* reflected in the _NET hints. Hidden windows all get pushed below
|
||||
* the screens fullscreen guard_window. */
|
||||
if (w->hidden)
|
||||
{
|
||||
g_array_append_val (all_hidden, top_level_window);
|
||||
if (w->client_type == META_WINDOW_CLIENT_TYPE_X11)
|
||||
{
|
||||
MetaStackWindow stack_window;
|
||||
|
||||
stack_window.any.type = META_WINDOW_CLIENT_TYPE_X11;
|
||||
stack_window.x11.xwindow = top_level_window;
|
||||
|
||||
g_array_append_val (x11_hidden_stack_windows, stack_window);
|
||||
g_array_append_val (x11_hidden, top_level_window);
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
g_array_append_val (all_root_children_stacked, stack_window);
|
||||
|
||||
/* build XRestackWindows() array from top to bottom */
|
||||
g_array_append_val (root_children_stacked, top_level_window);
|
||||
if (w->client_type == META_WINDOW_CLIENT_TYPE_X11)
|
||||
g_array_append_val (x11_root_children_stacked, top_level_window);
|
||||
else
|
||||
{
|
||||
MetaStackWindow *new;
|
||||
|
||||
/* So we can determine later if a cached stack window is
|
||||
* stale because the corresponding window has been freed we
|
||||
* associate a weak pointer with the new window. */
|
||||
new = &g_array_index (all_root_children_stacked, MetaStackWindow, all_root_children_stacked->len - 1);
|
||||
g_object_add_weak_pointer (G_OBJECT (new->wayland.meta_window),
|
||||
(gpointer *)&new->wayland.meta_window);
|
||||
}
|
||||
}
|
||||
|
||||
meta_topic (META_DEBUG_STACK, "\n");
|
||||
meta_pop_no_msg_prefix ();
|
||||
|
||||
/* All windows should be in some stacking order */
|
||||
if (stacked->len != stack->windows->len - n_override_redirect)
|
||||
/* All X windows should be in some stacking order */
|
||||
if (x11_stacked->len != stack->xwindows->len - n_override_redirect)
|
||||
meta_bug ("%u windows stacked, %u windows exist in stack\n",
|
||||
stacked->len, stack->windows->len);
|
||||
x11_stacked->len, stack->xwindows->len);
|
||||
|
||||
/* Sync to server */
|
||||
|
||||
meta_topic (META_DEBUG_STACK, "Restacking %u windows\n",
|
||||
root_children_stacked->len);
|
||||
all_root_children_stacked->len);
|
||||
|
||||
meta_error_trap_push (stack->screen->display);
|
||||
|
||||
if (stack->last_root_children_stacked == NULL)
|
||||
if (stack->last_all_root_children_stacked == NULL)
|
||||
{
|
||||
/* Just impose our stack, we don't know the previous state.
|
||||
* This involves a ton of circulate requests and may flicker.
|
||||
*/
|
||||
meta_topic (META_DEBUG_STACK, "Don't know last stack state, restacking everything\n");
|
||||
|
||||
if (root_children_stacked->len > 0)
|
||||
if (all_root_children_stacked->len > 1)
|
||||
{
|
||||
meta_stack_tracker_record_restack_windows (stack->screen->stack_tracker,
|
||||
(Window *) root_children_stacked->data,
|
||||
root_children_stacked->len,
|
||||
XNextRequest (stack->screen->display->xdisplay));
|
||||
gulong serial = 0;
|
||||
if (x11_root_children_stacked->len > 1)
|
||||
{
|
||||
serial = XNextRequest (stack->screen->display->xdisplay);
|
||||
XRestackWindows (stack->screen->display->xdisplay,
|
||||
(Window *) root_children_stacked->data,
|
||||
root_children_stacked->len);
|
||||
(Window *) x11_root_children_stacked->data,
|
||||
x11_root_children_stacked->len);
|
||||
}
|
||||
meta_stack_tracker_record_restack_windows (stack->screen->stack_tracker,
|
||||
(MetaStackWindow *) all_root_children_stacked->data,
|
||||
all_root_children_stacked->len,
|
||||
serial);
|
||||
}
|
||||
}
|
||||
else if (root_children_stacked->len > 0)
|
||||
else if (all_root_children_stacked->len > 0)
|
||||
{
|
||||
/* Try to do minimal window moves to get the stack in order */
|
||||
/* A point of note: these arrays include frames not client windows,
|
||||
@@ -1250,28 +1396,34 @@ stack_sync_to_server (MetaStack *stack)
|
||||
* was saved, then we may have inefficiency, but I don't think things
|
||||
* break...
|
||||
*/
|
||||
const Window *old_stack = (Window *) stack->last_root_children_stacked->data;
|
||||
const Window *new_stack = (Window *) root_children_stacked->data;
|
||||
const int old_len = stack->last_root_children_stacked->len;
|
||||
const int new_len = root_children_stacked->len;
|
||||
const Window *oldp = old_stack;
|
||||
const Window *newp = new_stack;
|
||||
const Window *old_end = old_stack + old_len;
|
||||
const Window *new_end = new_stack + new_len;
|
||||
Window last_window = None;
|
||||
|
||||
const MetaStackWindow *old_stack = (MetaStackWindow *) stack->last_all_root_children_stacked->data;
|
||||
const MetaStackWindow *new_stack = (MetaStackWindow *) all_root_children_stacked->data;
|
||||
const int old_len = stack->last_all_root_children_stacked->len;
|
||||
const int new_len = all_root_children_stacked->len;
|
||||
const MetaStackWindow *oldp = old_stack;
|
||||
const MetaStackWindow *newp = new_stack;
|
||||
const MetaStackWindow *old_end = old_stack + old_len;
|
||||
const MetaStackWindow *new_end = new_stack + new_len;
|
||||
Window last_xwindow = None;
|
||||
const MetaStackWindow *last_window = NULL;
|
||||
|
||||
while (oldp != old_end &&
|
||||
newp != new_end)
|
||||
{
|
||||
if (*oldp == *newp)
|
||||
if (meta_stack_window_equal (oldp, newp))
|
||||
{
|
||||
/* Stacks are the same here, move on */
|
||||
++oldp;
|
||||
last_window = *newp;
|
||||
if (newp->any.type == META_WINDOW_CLIENT_TYPE_X11)
|
||||
last_xwindow = newp->x11.xwindow;
|
||||
last_window = newp;
|
||||
++newp;
|
||||
}
|
||||
else if (meta_display_lookup_x_window (stack->screen->display,
|
||||
*oldp) == NULL)
|
||||
else if ((oldp->any.type == META_WINDOW_CLIENT_TYPE_X11 &&
|
||||
meta_display_lookup_x_window (stack->screen->display,
|
||||
oldp->x11.xwindow) == NULL) ||
|
||||
(oldp->any.type == META_WINDOW_CLIENT_TYPE_WAYLAND &&
|
||||
oldp->wayland.meta_window == NULL))
|
||||
{
|
||||
/* *oldp is no longer known to us (probably destroyed),
|
||||
* so we can just skip it
|
||||
@@ -1280,75 +1432,161 @@ stack_sync_to_server (MetaStack *stack)
|
||||
}
|
||||
else
|
||||
{
|
||||
/* Move *newp below last_window */
|
||||
if (last_window == None)
|
||||
/* Move *newp below the last_window */
|
||||
if (!last_window)
|
||||
{
|
||||
meta_topic (META_DEBUG_STACK, "Using window 0x%lx as topmost (but leaving it in-place)\n", *newp);
|
||||
meta_topic (META_DEBUG_STACK, "Using window 0x%lx as topmost (but leaving it in-place)\n",
|
||||
newp->x11.xwindow);
|
||||
|
||||
raise_window_relative_to_managed_windows (stack->screen,
|
||||
*newp);
|
||||
raise_window_relative_to_managed_windows (stack->screen, newp);
|
||||
}
|
||||
else if (newp->any.type == META_WINDOW_CLIENT_TYPE_X11 &&
|
||||
last_xwindow == None)
|
||||
{
|
||||
/* In this case we have an X window that we need to
|
||||
* put below a wayland window and this is the
|
||||
* topmost X window. */
|
||||
|
||||
/* In X terms (because this is the topmost X window)
|
||||
* we want to
|
||||
* raise_window_relative_to_managed_windows() to
|
||||
* ensure the X window is below override-redirect
|
||||
* pop-up windows.
|
||||
*
|
||||
* In Wayland terms we just want to ensure
|
||||
* newp is lowered below last_window (which
|
||||
* notably doesn't require an X request because we
|
||||
* know last_window isn't an X window).
|
||||
*/
|
||||
|
||||
raise_window_relative_to_managed_windows (stack->screen, newp);
|
||||
|
||||
meta_stack_tracker_record_lower_below (stack->screen->stack_tracker,
|
||||
newp, last_window,
|
||||
0); /* no x request serial */
|
||||
}
|
||||
else
|
||||
{
|
||||
/* This means that if last_window is dead, but not
|
||||
gulong serial = 0;
|
||||
|
||||
/* This means that if last_xwindow is dead, but not
|
||||
* *newp, then we fail to restack *newp; but on
|
||||
* unmanaging last_window, we'll fix it up.
|
||||
* unmanaging last_xwindow, we'll fix it up.
|
||||
*/
|
||||
|
||||
XWindowChanges changes;
|
||||
meta_topic (META_DEBUG_STACK, "Placing window 0x%lx below 0x%lx\n",
|
||||
newp->any.type == META_WINDOW_CLIENT_TYPE_X11 ? newp->x11.xwindow : 0,
|
||||
last_xwindow);
|
||||
|
||||
changes.sibling = last_window;
|
||||
if (newp->any.type == META_WINDOW_CLIENT_TYPE_X11)
|
||||
{
|
||||
XWindowChanges changes;
|
||||
serial = XNextRequest (stack->screen->display->xdisplay);
|
||||
|
||||
changes.sibling = last_xwindow;
|
||||
changes.stack_mode = Below;
|
||||
|
||||
meta_topic (META_DEBUG_STACK, "Placing window 0x%lx below 0x%lx\n",
|
||||
*newp, last_window);
|
||||
|
||||
meta_stack_tracker_record_lower_below (stack->screen->stack_tracker,
|
||||
*newp, last_window,
|
||||
XNextRequest (stack->screen->display->xdisplay));
|
||||
XConfigureWindow (stack->screen->display->xdisplay,
|
||||
*newp,
|
||||
newp->x11.xwindow,
|
||||
CWSibling | CWStackMode,
|
||||
&changes);
|
||||
}
|
||||
|
||||
last_window = *newp;
|
||||
meta_stack_tracker_record_lower_below (stack->screen->stack_tracker,
|
||||
newp, last_window,
|
||||
serial);
|
||||
}
|
||||
|
||||
if (newp->any.type == META_WINDOW_CLIENT_TYPE_X11)
|
||||
last_xwindow = newp->x11.xwindow;
|
||||
last_window = newp;
|
||||
++newp;
|
||||
}
|
||||
}
|
||||
|
||||
if (newp != new_end)
|
||||
{
|
||||
const MetaStackWindow *x_ref;
|
||||
unsigned long serial = 0;
|
||||
|
||||
/* Restack remaining windows */
|
||||
meta_topic (META_DEBUG_STACK, "Restacking remaining %d windows\n",
|
||||
(int) (new_end - newp));
|
||||
|
||||
/* rewind until we find the last stacked X window that we can use
|
||||
* as a reference point for re-stacking remaining X windows */
|
||||
if (newp != new_stack)
|
||||
for (x_ref = newp - 1;
|
||||
x_ref->any.type != META_WINDOW_CLIENT_TYPE_X11 && x_ref > new_stack;
|
||||
x_ref--)
|
||||
;
|
||||
else
|
||||
x_ref = new_stack;
|
||||
|
||||
/* If we didn't find an X window looking backwards then walk forwards
|
||||
* through the remaining windows to find the first remaining X window
|
||||
* instead. */
|
||||
if (x_ref->any.type != META_WINDOW_CLIENT_TYPE_X11)
|
||||
{
|
||||
for (x_ref = newp;
|
||||
x_ref->any.type != META_WINDOW_CLIENT_TYPE_X11 && x_ref > new_stack;
|
||||
x_ref++)
|
||||
;
|
||||
}
|
||||
|
||||
/* If there are any X windows remaining unstacked then restack them */
|
||||
if (x_ref->any.type == META_WINDOW_CLIENT_TYPE_X11)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = x11_root_children_stacked->len - 1; i; i--)
|
||||
{
|
||||
Window *reference = &g_array_index (x11_root_children_stacked, Window, i);
|
||||
|
||||
if (*reference == x_ref->x11.xwindow)
|
||||
{
|
||||
int n = x11_root_children_stacked->len - i;
|
||||
|
||||
/* There's no point restacking if there's only one X window */
|
||||
if (n == 1)
|
||||
break;
|
||||
|
||||
serial = XNextRequest (stack->screen->display->xdisplay);
|
||||
XRestackWindows (stack->screen->display->xdisplay,
|
||||
reference, n);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* We need to include an already-stacked window
|
||||
* in the restack call, so we get in the proper position
|
||||
* with respect to it.
|
||||
*/
|
||||
if (newp != new_stack)
|
||||
--newp;
|
||||
newp = MIN (newp - 1, x_ref);
|
||||
meta_stack_tracker_record_restack_windows (stack->screen->stack_tracker,
|
||||
(Window *) newp, new_end - newp,
|
||||
XNextRequest (stack->screen->display->xdisplay));
|
||||
XRestackWindows (stack->screen->display->xdisplay,
|
||||
(Window *) newp, new_end - newp);
|
||||
newp, new_end - newp,
|
||||
serial);
|
||||
}
|
||||
}
|
||||
|
||||
/* Push hidden windows to the bottom of the stack under the guard window */
|
||||
/* Push hidden X windows to the bottom of the stack under the guard window */
|
||||
guard_stack_window.any.type = META_WINDOW_CLIENT_TYPE_X11;
|
||||
guard_stack_window.x11.xwindow = stack->screen->guard_window;
|
||||
meta_stack_tracker_record_lower (stack->screen->stack_tracker,
|
||||
stack->screen->guard_window,
|
||||
&guard_stack_window,
|
||||
XNextRequest (stack->screen->display->xdisplay));
|
||||
XLowerWindow (stack->screen->display->xdisplay, stack->screen->guard_window);
|
||||
meta_stack_tracker_record_restack_windows (stack->screen->stack_tracker,
|
||||
(Window *)all_hidden->data,
|
||||
all_hidden->len,
|
||||
(MetaStackWindow *)x11_hidden_stack_windows->data,
|
||||
x11_hidden_stack_windows->len,
|
||||
XNextRequest (stack->screen->display->xdisplay));
|
||||
XRestackWindows (stack->screen->display->xdisplay,
|
||||
(Window *)all_hidden->data,
|
||||
all_hidden->len);
|
||||
g_array_free (all_hidden, TRUE);
|
||||
(Window *)x11_hidden->data,
|
||||
x11_hidden->len);
|
||||
g_array_free (x11_hidden, TRUE);
|
||||
g_array_free (x11_hidden_stack_windows, TRUE);
|
||||
|
||||
meta_error_trap_pop (stack->screen->display);
|
||||
/* on error, a window was destroyed; it should eventually
|
||||
@@ -1363,21 +1601,23 @@ stack_sync_to_server (MetaStack *stack)
|
||||
stack->screen->display->atom__NET_CLIENT_LIST,
|
||||
XA_WINDOW,
|
||||
32, PropModeReplace,
|
||||
(unsigned char *)stack->windows->data,
|
||||
stack->windows->len);
|
||||
(unsigned char *)stack->xwindows->data,
|
||||
stack->xwindows->len);
|
||||
XChangeProperty (stack->screen->display->xdisplay,
|
||||
stack->screen->xroot,
|
||||
stack->screen->display->atom__NET_CLIENT_LIST_STACKING,
|
||||
XA_WINDOW,
|
||||
32, PropModeReplace,
|
||||
(unsigned char *)stacked->data,
|
||||
stacked->len);
|
||||
(unsigned char *)x11_stacked->data,
|
||||
x11_stacked->len);
|
||||
|
||||
g_array_free (stacked, TRUE);
|
||||
g_array_free (x11_stacked, TRUE);
|
||||
|
||||
if (stack->last_root_children_stacked)
|
||||
g_array_free (stack->last_root_children_stacked, TRUE);
|
||||
stack->last_root_children_stacked = root_children_stacked;
|
||||
if (stack->last_all_root_children_stacked)
|
||||
free_last_all_root_children_stacked_cache (stack);
|
||||
stack->last_all_root_children_stacked = all_root_children_stacked;
|
||||
|
||||
g_array_free (x11_root_children_stacked, TRUE);
|
||||
|
||||
/* That was scary... */
|
||||
}
|
||||
@@ -1738,7 +1978,7 @@ meta_stack_set_positions (MetaStack *stack,
|
||||
meta_topic (META_DEBUG_STACK,
|
||||
"Reset the stack positions of (nearly) all windows\n");
|
||||
|
||||
stack_sync_to_server (stack);
|
||||
stack_sync_to_xserver (stack);
|
||||
meta_stack_update_window_tile_matches (stack, NULL);
|
||||
}
|
||||
|
||||
@@ -1801,7 +2041,7 @@ meta_window_set_stack_position (MetaWindow *window,
|
||||
int position)
|
||||
{
|
||||
meta_window_set_stack_position_no_sync (window, position);
|
||||
stack_sync_to_server (window->screen->stack);
|
||||
stack_sync_to_xserver (window->screen->stack);
|
||||
meta_stack_update_window_tile_matches (window->screen->stack,
|
||||
window->screen->active_workspace);
|
||||
}
|
||||
|
||||
@@ -60,7 +60,7 @@ struct _MetaStack
|
||||
* A sequence of all the Windows (X handles, not MetaWindows) of the windows
|
||||
* we manage, sorted in order. Suitable to be passed into _NET_CLIENT_LIST.
|
||||
*/
|
||||
GArray *windows;
|
||||
GArray *xwindows;
|
||||
|
||||
/** The MetaWindows of the windows we manage, sorted in order. */
|
||||
GList *sorted;
|
||||
@@ -99,7 +99,7 @@ struct _MetaStack
|
||||
* The last-known stack of all windows, bottom to top. We cache it here
|
||||
* so that subsequent times we'll be able to do incremental moves.
|
||||
*/
|
||||
GArray *last_root_children_stacked;
|
||||
GArray *last_all_root_children_stacked;
|
||||
|
||||
/**
|
||||
* Number of stack positions; same as the length of added, but
|
||||
|
||||
@@ -55,6 +55,7 @@ static gint verbose_topics = 0;
|
||||
static gboolean is_debugging = FALSE;
|
||||
static gboolean replace_current = FALSE;
|
||||
static int no_prefix = 0;
|
||||
static gboolean is_wayland_compositor = FALSE;
|
||||
|
||||
#ifdef WITH_VERBOSE_MODE
|
||||
static FILE* logfile = NULL;
|
||||
@@ -194,6 +195,18 @@ meta_set_replace_current_wm (gboolean setting)
|
||||
replace_current = setting;
|
||||
}
|
||||
|
||||
gboolean
|
||||
meta_is_wayland_compositor (void)
|
||||
{
|
||||
return is_wayland_compositor;
|
||||
}
|
||||
|
||||
void
|
||||
meta_set_is_wayland_compositor (gboolean value)
|
||||
{
|
||||
is_wayland_compositor = value;
|
||||
}
|
||||
|
||||
char *
|
||||
meta_g_utf8_strndup (const gchar *src,
|
||||
gsize n)
|
||||
|
||||
@@ -44,6 +44,7 @@
|
||||
#include <X11/Xutil.h>
|
||||
#include <cairo.h>
|
||||
#include <gdk-pixbuf/gdk-pixbuf.h>
|
||||
#include "meta-wayland-types.h"
|
||||
|
||||
typedef struct _MetaWindowQueue MetaWindowQueue;
|
||||
|
||||
@@ -69,6 +70,7 @@ typedef enum {
|
||||
_NET_WM_BYPASS_COMPOSITOR_HINT_OFF = 2,
|
||||
} MetaBypassCompositorHintValue;
|
||||
|
||||
|
||||
struct _MetaWindow
|
||||
{
|
||||
GObject parent_instance;
|
||||
@@ -77,6 +79,10 @@ struct _MetaWindow
|
||||
MetaScreen *screen;
|
||||
const MetaMonitorInfo *monitor;
|
||||
MetaWorkspace *workspace;
|
||||
MetaWindowClientType client_type;
|
||||
#ifdef HAVE_WAYLAND
|
||||
MetaWaylandSurface *surface;
|
||||
#endif
|
||||
Window xwindow;
|
||||
/* may be NULL! not all windows get decorated */
|
||||
MetaFrame *frame;
|
||||
@@ -325,9 +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 shape mask */
|
||||
guint has_shape : 1;
|
||||
|
||||
/* icon props have changed */
|
||||
guint need_reread_icon : 1;
|
||||
|
||||
@@ -349,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;
|
||||
@@ -490,6 +499,10 @@ MetaWindow* meta_window_new_with_attrs (MetaDisplay *display,
|
||||
gboolean must_be_viewable,
|
||||
MetaCompEffect effect,
|
||||
XWindowAttributes *attrs);
|
||||
MetaWindow *meta_window_new_for_wayland (MetaDisplay *display,
|
||||
int width,
|
||||
int height,
|
||||
MetaWaylandSurface *surface);
|
||||
void meta_window_unmanage (MetaWindow *window,
|
||||
guint32 timestamp);
|
||||
void meta_window_calc_showing (MetaWindow *window);
|
||||
@@ -665,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);
|
||||
|
||||
@@ -679,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
|
||||
|
||||
@@ -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",
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
35
src/idle-monitor.xml
Normal file
35
src/idle-monitor.xml
Normal file
@@ -0,0 +1,35 @@
|
||||
<!DOCTYPE node PUBLIC
|
||||
'-//freedesktop//DTD D-BUS Object Introspection 1.0//EN'
|
||||
'http://www.freedesktop.org/standards/dbus/1.0/introspect.dtd'>
|
||||
<node>
|
||||
<!--
|
||||
org.gnome.Mutter.IdleMonitor:
|
||||
@short_description: idle monitor interface
|
||||
|
||||
This interface is used by gnome-desktop to implement
|
||||
user activity monitoring.
|
||||
-->
|
||||
|
||||
<interface name="org.gnome.Mutter.IdleMonitor">
|
||||
<method name="GetIdletime">
|
||||
<arg name="idletime" direction="out" type="t"/>
|
||||
</method>
|
||||
|
||||
<method name="AddIdleWatch">
|
||||
<arg name="interval" direction="in" type="t" />
|
||||
<arg name="id" direction="out" type="u" />
|
||||
</method>
|
||||
|
||||
<method name="AddUserActiveWatch">
|
||||
<arg name="id" direction="out" type="u" />
|
||||
</method>
|
||||
|
||||
<method name="RemoveWatch">
|
||||
<arg name="id" direction="in" type="u" />
|
||||
</method>
|
||||
|
||||
<signal name="WatchFired">
|
||||
<arg name="id" direction="out" type="u" />
|
||||
</signal>
|
||||
</interface>
|
||||
</node>
|
||||
18
src/libmutter-wayland.pc.in
Normal file
18
src/libmutter-wayland.pc.in
Normal file
@@ -0,0 +1,18 @@
|
||||
prefix=@prefix@
|
||||
exec_prefix=@exec_prefix@
|
||||
libdir=@libdir@
|
||||
includedir=@includedir@
|
||||
girdir=@libdir@/mutter-wayland
|
||||
typelibdir=@libdir@/mutter-wayland
|
||||
|
||||
mutter_major_version=@MUTTER_MAJOR_VERSION@
|
||||
mutter_minor_version=@MUTTER_MINOR_VERSION@
|
||||
mutter_micro_version=@MUTTER_MICRO_VERSION@
|
||||
mutter_plugin_api_version=@MUTTER_PLUGIN_API_VERSION@
|
||||
|
||||
Name: libmutter-wayland
|
||||
Description: Mutter window manager library (Wayland branch)
|
||||
Requires: gsettings-desktop-schemas gtk+-3.0 @CLUTTER_PACKAGE@ x11 wayland-server
|
||||
Version: @VERSION@
|
||||
Libs: -L${libdir} -lmutter-wayland
|
||||
Cflags: -I${includedir}/mutter-wayland -DMUTTER_MAJOR_VERSION=${mutter_major_version} -DMUTTER_MINOR_VERSION=${mutter_minor_version} -DMUTTER_MICRO_VERSION=${mutter_micro_version} -DMUTTER_PLUGIN_API_VERSION=${mutter_plugin_api_version}
|
||||
@@ -1,18 +0,0 @@
|
||||
prefix=@prefix@
|
||||
exec_prefix=@exec_prefix@
|
||||
libdir=@libdir@
|
||||
includedir=@includedir@
|
||||
girdir=@libdir@/mutter
|
||||
typelibdir=@libdir@/mutter
|
||||
|
||||
mutter_major_version=@MUTTER_MAJOR_VERSION@
|
||||
mutter_minor_version=@MUTTER_MINOR_VERSION@
|
||||
mutter_micro_version=@MUTTER_MICRO_VERSION@
|
||||
mutter_plugin_api_version=@MUTTER_PLUGIN_API_VERSION@
|
||||
|
||||
Name: libmutter
|
||||
Description: Mutter window manager library
|
||||
Requires: gsettings-desktop-schemas gtk+-3.0 @CLUTTER_PACKAGE@ x11
|
||||
Version: @VERSION@
|
||||
Libs: -L${libdir} -lmutter
|
||||
Cflags: -I${includedir}/mutter -DMUTTER_MAJOR_VERSION=${mutter_major_version} -DMUTTER_MINOR_VERSION=${mutter_minor_version} -DMUTTER_MICRO_VERSION=${mutter_micro_version} -DMUTTER_PLUGIN_API_VERSION=${mutter_plugin_api_version}
|
||||
50
src/meta/meta-cursor-tracker.h
Normal file
50
src/meta/meta-cursor-tracker.h
Normal file
@@ -0,0 +1,50 @@
|
||||
/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
|
||||
|
||||
/*
|
||||
* 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.
|
||||
*
|
||||
* Author: Giovanni Campagna <gcampagn@redhat.com>
|
||||
*/
|
||||
|
||||
#ifndef META_CURSOR_TRACKER_H
|
||||
#define META_CURSOR_TRACKER_H
|
||||
|
||||
#include <glib-object.h>
|
||||
#include <meta/types.h>
|
||||
#include <meta/workspace.h>
|
||||
#include <cogl/cogl.h>
|
||||
|
||||
#define META_TYPE_CURSOR_TRACKER (meta_cursor_tracker_get_type ())
|
||||
#define META_CURSOR_TRACKER(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), META_TYPE_CURSOR_TRACKER, MetaCursorTracker))
|
||||
#define META_CURSOR_TRACKER_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), META_TYPE_CURSOR_TRACKER, MetaCursorTrackerClass))
|
||||
#define META_IS_CURSOR_TRACKER(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), META_TYPE_CURSOR_TRACKER))
|
||||
#define META_IS_CURSOR_TRACKER_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), META_TYPE_CURSOR_TRACKER))
|
||||
#define META_CURSOR_TRACKER_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), META_TYPE_CURSOR_TRACKER, MetaCursorTrackerClass))
|
||||
|
||||
typedef struct _MetaCursorTrackerClass MetaCursorTrackerClass;
|
||||
|
||||
GType meta_cursor_tracker_get_type (void);
|
||||
|
||||
MetaCursorTracker *meta_cursor_tracker_get_for_screen (MetaScreen *screen);
|
||||
|
||||
void meta_cursor_tracker_get_hot (MetaCursorTracker *tracker,
|
||||
int *x,
|
||||
int *y);
|
||||
CoglTexture *meta_cursor_tracker_get_sprite (MetaCursorTracker *tracker);
|
||||
|
||||
#endif
|
||||
62
src/meta/meta-idle-monitor.h
Normal file
62
src/meta/meta-idle-monitor.h
Normal file
@@ -0,0 +1,62 @@
|
||||
/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
|
||||
|
||||
/*
|
||||
* Copyright 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_IDLE_MONITOR_H
|
||||
#define META_IDLE_MONITOR_H
|
||||
|
||||
#include <glib-object.h>
|
||||
#include <meta/types.h>
|
||||
|
||||
#define META_TYPE_IDLE_MONITOR (meta_idle_monitor_get_type ())
|
||||
#define META_IDLE_MONITOR(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), META_TYPE_IDLE_MONITOR, MetaIdleMonitor))
|
||||
#define META_IDLE_MONITOR_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), META_TYPE_IDLE_MONITOR, MetaIdleMonitorClass))
|
||||
#define META_IS_IDLE_MONITOR(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), META_TYPE_IDLE_MONITOR))
|
||||
#define META_IS_IDLE_MONITOR_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), META_TYPE_IDLE_MONITOR))
|
||||
#define META_IDLE_MONITOR_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), META_TYPE_IDLE_MONITOR, MetaIdleMonitorClass))
|
||||
|
||||
typedef struct _MetaIdleMonitor MetaIdleMonitor;
|
||||
typedef struct _MetaIdleMonitorClass MetaIdleMonitorClass;
|
||||
|
||||
GType meta_idle_monitor_get_type (void);
|
||||
|
||||
typedef void (*MetaIdleMonitorWatchFunc) (MetaIdleMonitor *monitor,
|
||||
guint watch_id,
|
||||
gpointer user_data);
|
||||
|
||||
MetaIdleMonitor *meta_idle_monitor_get_core (void);
|
||||
MetaIdleMonitor *meta_idle_monitor_get_for_device (int device_id);
|
||||
|
||||
guint meta_idle_monitor_add_idle_watch (MetaIdleMonitor *monitor,
|
||||
guint64 interval_msec,
|
||||
MetaIdleMonitorWatchFunc callback,
|
||||
gpointer user_data,
|
||||
GDestroyNotify notify);
|
||||
|
||||
guint meta_idle_monitor_add_user_active_watch (MetaIdleMonitor *monitor,
|
||||
MetaIdleMonitorWatchFunc callback,
|
||||
gpointer user_data,
|
||||
GDestroyNotify notify);
|
||||
|
||||
void meta_idle_monitor_remove_watch (MetaIdleMonitor *monitor,
|
||||
guint id);
|
||||
gint64 meta_idle_monitor_get_idletime (MetaIdleMonitor *monitor);
|
||||
|
||||
#endif
|
||||
@@ -396,8 +396,6 @@ typedef enum {
|
||||
|
||||
gboolean
|
||||
meta_plugin_begin_modal (MetaPlugin *plugin,
|
||||
Window grab_window,
|
||||
Cursor cursor,
|
||||
MetaModalOptions options,
|
||||
guint32 timestamp);
|
||||
|
||||
|
||||
@@ -64,29 +64,29 @@ struct _MetaShapedTexture
|
||||
|
||||
GType meta_shaped_texture_get_type (void) G_GNUC_CONST;
|
||||
|
||||
ClutterActor *meta_shaped_texture_new (void);
|
||||
|
||||
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);
|
||||
|
||||
void meta_shaped_texture_set_pixmap (MetaShapedTexture *stex,
|
||||
Pixmap pixmap);
|
||||
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);
|
||||
|
||||
void meta_shaped_texture_set_mask_texture (MetaShapedTexture *stex,
|
||||
CoglTexture *mask_texture);
|
||||
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);
|
||||
|
||||
|
||||
@@ -38,5 +38,6 @@ typedef struct _MetaWorkspace MetaWorkspace;
|
||||
*/
|
||||
typedef struct _MetaGroup MetaGroup;
|
||||
typedef struct _MetaKeyBinding MetaKeyBinding;
|
||||
typedef struct _MetaCursorTracker MetaCursorTracker;
|
||||
|
||||
#endif
|
||||
|
||||
@@ -37,6 +37,8 @@ void meta_set_debugging (gboolean setting);
|
||||
gboolean meta_is_syncing (void);
|
||||
void meta_set_syncing (gboolean setting);
|
||||
void meta_set_replace_current_wm (gboolean setting);
|
||||
gboolean meta_is_wayland_compositor (void);
|
||||
void meta_set_is_wayland_compositor (gboolean setting);
|
||||
|
||||
void meta_debug_spew_real (const char *format,
|
||||
...) G_GNUC_PRINTF (1, 2);
|
||||
|
||||
@@ -81,6 +81,16 @@ typedef enum
|
||||
META_MAXIMIZE_VERTICAL = 1 << 1
|
||||
} MetaMaximizeFlags;
|
||||
|
||||
/**
|
||||
* MetaWindowClientType:
|
||||
* @META_WINDOW_CLIENT_TYPE_WAYLAND: A Wayland based window
|
||||
* @META_WINDOW_CLIENT_TYPE_X11: An X11 based window
|
||||
*/
|
||||
typedef enum {
|
||||
META_WINDOW_CLIENT_TYPE_WAYLAND,
|
||||
META_WINDOW_CLIENT_TYPE_X11
|
||||
} MetaWindowClientType;
|
||||
|
||||
#define META_TYPE_WINDOW (meta_window_get_type ())
|
||||
#define META_WINDOW(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), META_TYPE_WINDOW, MetaWindow))
|
||||
#define META_WINDOW_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), META_TYPE_WINDOW, MetaWindowClass))
|
||||
|
||||
@@ -1,17 +0,0 @@
|
||||
prefix=@prefix@
|
||||
exec_prefix=@exec_prefix@
|
||||
libdir=@libdir@
|
||||
includedir=@includedir@
|
||||
plugindir=@MUTTER_PLUGIN_DIR@
|
||||
libgnome_serverdir=@libexecdir@
|
||||
mutter_major_version=@MUTTER_MAJOR_VERSION@
|
||||
mutter_minor_version=@MUTTER_MINOR_VERSION@
|
||||
mutter_micro_version=@MUTTER_MICRO_VERSION@
|
||||
mutter_plugin_api_version=@MUTTER_PLUGIN_API_VERSION@
|
||||
|
||||
Name: mutter-plugins
|
||||
Description: Dev parameters for mutter plugins
|
||||
Requires: @CLUTTER_PACKAGE@
|
||||
Version: @VERSION@
|
||||
Libs: @CLUTTER_LIBS@
|
||||
Cflags: @CLUTTER_CFLAGS@ -DWITH_CLUTTER -I${includedir}/mutter/mutter-private -DMUTTER_MAJOR_VERSION=${mutter_major_version} -DMUTTER_MINOR_VERSION=${mutter_minor_version} -DMUTTER_MICRO_VERSION=${mutter_micro_version} -DMUTTER_PLUGIN_API_VERSION=${mutter_plugin_api_version} -DMUTTER_PLUGIN_DIR=\"${plugindir}\"
|
||||
@@ -1,7 +1,7 @@
|
||||
[Desktop Entry]
|
||||
Type=Application
|
||||
_Name=Mutter
|
||||
Exec=mutter
|
||||
_Name=Mutter (wayland compositor)
|
||||
Exec=mutter-launch -- mutter --nested
|
||||
NoDisplay=true
|
||||
# name of loadable control center module
|
||||
X-GNOME-WMSettingsModule=metacity
|
||||
@@ -12,6 +12,5 @@ X-GnomeWMSettingsLibrary=metacity
|
||||
X-GNOME-Bugzilla-Bugzilla=GNOME
|
||||
X-GNOME-Bugzilla-Product=mutter
|
||||
X-GNOME-Bugzilla-Component=general
|
||||
X-GNOME-Autostart-Phase=WindowManager
|
||||
X-GNOME-Provides=windowmanager
|
||||
X-GNOME-Autostart-Phase=DisplayServer
|
||||
X-GNOME-Autostart-Notify=true
|
||||
@@ -1,20 +0,0 @@
|
||||
[Desktop Entry]
|
||||
Type=Application
|
||||
_Name=Mutter
|
||||
Exec=mutter
|
||||
# name of loadable control center module
|
||||
X-GNOME-WMSettingsModule=metacity
|
||||
# name we put on the WM spec check window
|
||||
X-GNOME-WMName=Mutter
|
||||
# back compat only
|
||||
X-GnomeWMSettingsLibrary=metacity
|
||||
X-GNOME-Bugzilla-Bugzilla=GNOME
|
||||
X-GNOME-Bugzilla-Product=mutter
|
||||
X-GNOME-Bugzilla-Component=general
|
||||
X-GNOME-Autostart-Phase=WindowManager
|
||||
X-GNOME-Provides=windowmanager
|
||||
X-GNOME-Autostart-Notify=true
|
||||
|
||||
[Window Manager]
|
||||
SessionManaged=true
|
||||
|
||||
33
src/org.gnome.mutter.wayland.gschema.xml.in
Normal file
33
src/org.gnome.mutter.wayland.gschema.xml.in
Normal 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>
|
||||
@@ -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])
|
||||
{
|
||||
|
||||
@@ -56,6 +56,8 @@ struct _MetaUI
|
||||
void
|
||||
meta_ui_init (void)
|
||||
{
|
||||
gdk_set_allowed_backends ("x11");
|
||||
|
||||
if (!gtk_init_check (NULL, NULL))
|
||||
meta_fatal ("Unable to open X display %s\n", XDisplayName (NULL));
|
||||
}
|
||||
|
||||
549
src/wayland/meta-wayland-data-device.c
Normal file
549
src/wayland/meta-wayland-data-device.c
Normal file
@@ -0,0 +1,549 @@
|
||||
/*
|
||||
* Copyright © 2011 Kristian Høgsberg
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
/* The file is based on src/data-device.c from Weston */
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
#include <stdio.h>
|
||||
#include <glib.h>
|
||||
|
||||
#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,
|
||||
struct wl_resource *resource,
|
||||
guint32 serial,
|
||||
const char *mime_type)
|
||||
{
|
||||
MetaWaylandDataOffer *offer = wl_resource_get_user_data (resource);
|
||||
|
||||
/* FIXME: Check that client is currently focused by the input
|
||||
* device that is currently dragging this data source. Should
|
||||
* this be a wl_data_device request? */
|
||||
|
||||
if (offer->source)
|
||||
offer->source->accept (offer->source, serial, mime_type);
|
||||
}
|
||||
|
||||
static void
|
||||
data_offer_receive (struct wl_client *client, struct wl_resource *resource,
|
||||
const char *mime_type, int32_t fd)
|
||||
{
|
||||
MetaWaylandDataOffer *offer = wl_resource_get_user_data (resource);
|
||||
|
||||
if (offer->source)
|
||||
offer->source->send (offer->source, mime_type, fd);
|
||||
else
|
||||
close (fd);
|
||||
}
|
||||
|
||||
static void
|
||||
data_offer_destroy (struct wl_client *client, struct wl_resource *resource)
|
||||
{
|
||||
wl_resource_destroy (resource);
|
||||
}
|
||||
|
||||
static const struct wl_data_offer_interface data_offer_interface = {
|
||||
data_offer_accept,
|
||||
data_offer_receive,
|
||||
data_offer_destroy,
|
||||
};
|
||||
|
||||
static void
|
||||
destroy_data_offer (struct wl_resource *resource)
|
||||
{
|
||||
MetaWaylandDataOffer *offer = wl_resource_get_user_data (resource);
|
||||
|
||||
if (offer->source)
|
||||
wl_list_remove (&offer->source_destroy_listener.link);
|
||||
free (offer);
|
||||
}
|
||||
|
||||
static void
|
||||
destroy_offer_data_source (struct wl_listener *listener, void *data)
|
||||
{
|
||||
MetaWaylandDataOffer *offer;
|
||||
|
||||
offer = wl_container_of (listener, offer, source_destroy_listener);
|
||||
|
||||
offer->source = NULL;
|
||||
}
|
||||
|
||||
static struct wl_resource *
|
||||
meta_wayland_data_source_send_offer (MetaWaylandDataSource *source,
|
||||
struct wl_resource *target)
|
||||
{
|
||||
MetaWaylandDataOffer *offer;
|
||||
char **p;
|
||||
|
||||
offer = malloc (sizeof *offer);
|
||||
if (offer == NULL)
|
||||
return NULL;
|
||||
|
||||
offer->source = source;
|
||||
offer->source_destroy_listener.notify = destroy_offer_data_source;
|
||||
|
||||
offer->resource = wl_client_add_object (wl_resource_get_client (target),
|
||||
&wl_data_offer_interface,
|
||||
&data_offer_interface,
|
||||
0,
|
||||
offer);
|
||||
wl_resource_set_destructor (offer->resource, destroy_data_offer);
|
||||
wl_resource_add_destroy_listener (source->resource,
|
||||
&offer->source_destroy_listener);
|
||||
|
||||
wl_data_device_send_data_offer (target, offer->resource);
|
||||
|
||||
wl_array_for_each (p, &source->mime_types)
|
||||
wl_data_offer_send_offer (offer->resource, *p);
|
||||
|
||||
return offer->resource;
|
||||
}
|
||||
|
||||
static void
|
||||
data_source_offer (struct wl_client *client,
|
||||
struct wl_resource *resource, const char *type)
|
||||
{
|
||||
MetaWaylandDataSource *source = wl_resource_get_user_data (resource);
|
||||
char **p;
|
||||
|
||||
p = wl_array_add (&source->mime_types, sizeof *p);
|
||||
if (p)
|
||||
*p = strdup (type);
|
||||
if (!p || !*p)
|
||||
wl_resource_post_no_memory (resource);
|
||||
}
|
||||
|
||||
static void
|
||||
data_source_destroy (struct wl_client *client, struct wl_resource *resource)
|
||||
{
|
||||
wl_resource_destroy (resource);
|
||||
}
|
||||
|
||||
static struct wl_data_source_interface data_source_interface = {
|
||||
data_source_offer,
|
||||
data_source_destroy
|
||||
};
|
||||
|
||||
static void
|
||||
destroy_drag_focus (struct wl_listener *listener, void *data)
|
||||
{
|
||||
MetaWaylandSeat *seat = wl_container_of (listener, seat, drag_focus_listener);
|
||||
|
||||
seat->drag_focus_resource = NULL;
|
||||
}
|
||||
|
||||
static void
|
||||
drag_grab_focus (MetaWaylandPointerGrab *grab,
|
||||
MetaWaylandSurface *surface,
|
||||
wl_fixed_t x,
|
||||
wl_fixed_t y)
|
||||
{
|
||||
MetaWaylandSeat *seat = wl_container_of (grab, seat, drag_grab);
|
||||
struct wl_resource *resource, *offer = NULL;
|
||||
struct wl_display *display;
|
||||
guint32 serial;
|
||||
|
||||
if (seat->drag_focus_resource)
|
||||
{
|
||||
wl_data_device_send_leave (seat->drag_focus_resource);
|
||||
wl_list_remove (&seat->drag_focus_listener.link);
|
||||
seat->drag_focus_resource = NULL;
|
||||
seat->drag_focus = NULL;
|
||||
}
|
||||
|
||||
if (!surface)
|
||||
return;
|
||||
|
||||
if (!seat->drag_data_source &&
|
||||
wl_resource_get_client (surface->resource) != seat->drag_client)
|
||||
return;
|
||||
|
||||
resource =
|
||||
wl_resource_find_for_client (&seat->drag_resource_list,
|
||||
wl_resource_get_client (surface->resource));
|
||||
if (!resource)
|
||||
return;
|
||||
|
||||
display = wl_client_get_display (wl_resource_get_client (resource));
|
||||
serial = wl_display_next_serial (display);
|
||||
|
||||
if (seat->drag_data_source)
|
||||
offer = meta_wayland_data_source_send_offer (seat->drag_data_source,
|
||||
resource);
|
||||
|
||||
wl_data_device_send_enter (resource, serial, surface->resource,
|
||||
x, y, offer);
|
||||
|
||||
seat->drag_focus = surface;
|
||||
seat->drag_focus_listener.notify = destroy_drag_focus;
|
||||
wl_resource_add_destroy_listener (resource, &seat->drag_focus_listener);
|
||||
seat->drag_focus_resource = resource;
|
||||
grab->focus = surface;
|
||||
}
|
||||
|
||||
static void
|
||||
drag_grab_motion (MetaWaylandPointerGrab *grab,
|
||||
guint32 time, wl_fixed_t x, wl_fixed_t y)
|
||||
{
|
||||
MetaWaylandSeat *seat = wl_container_of (grab, seat, drag_grab);
|
||||
|
||||
if (seat->drag_focus_resource)
|
||||
wl_data_device_send_motion (seat->drag_focus_resource, time, x, y);
|
||||
}
|
||||
|
||||
static void
|
||||
data_device_end_drag_grab (MetaWaylandSeat *seat)
|
||||
{
|
||||
if (seat->drag_surface)
|
||||
{
|
||||
seat->drag_surface = NULL;
|
||||
wl_signal_emit (&seat->drag_icon_signal, NULL);
|
||||
wl_list_remove (&seat->drag_icon_listener.link);
|
||||
}
|
||||
|
||||
drag_grab_focus (&seat->drag_grab, NULL,
|
||||
wl_fixed_from_int (0), wl_fixed_from_int (0));
|
||||
|
||||
meta_wayland_pointer_end_grab (&seat->pointer);
|
||||
|
||||
seat->drag_data_source = NULL;
|
||||
seat->drag_client = NULL;
|
||||
}
|
||||
|
||||
static void
|
||||
drag_grab_button (MetaWaylandPointerGrab *grab,
|
||||
guint32 time, guint32 button, guint32 state_w)
|
||||
{
|
||||
MetaWaylandSeat *seat = wl_container_of (grab, seat, drag_grab);
|
||||
enum wl_pointer_button_state state = state_w;
|
||||
|
||||
if (seat->drag_focus_resource &&
|
||||
seat->pointer.grab_button == button &&
|
||||
state == WL_POINTER_BUTTON_STATE_RELEASED)
|
||||
wl_data_device_send_drop (seat->drag_focus_resource);
|
||||
|
||||
if (seat->pointer.button_count == 0 &&
|
||||
state == WL_POINTER_BUTTON_STATE_RELEASED)
|
||||
{
|
||||
if (seat->drag_data_source)
|
||||
wl_list_remove (&seat->drag_data_source_listener.link);
|
||||
data_device_end_drag_grab (seat);
|
||||
}
|
||||
}
|
||||
|
||||
static const MetaWaylandPointerGrabInterface drag_grab_interface = {
|
||||
drag_grab_focus,
|
||||
drag_grab_motion,
|
||||
drag_grab_button,
|
||||
};
|
||||
|
||||
static void
|
||||
destroy_data_device_source (struct wl_listener *listener, void *data)
|
||||
{
|
||||
MetaWaylandSeat *seat =
|
||||
wl_container_of (listener, seat, drag_data_source_listener);
|
||||
|
||||
data_device_end_drag_grab (seat);
|
||||
}
|
||||
|
||||
static void
|
||||
destroy_data_device_icon (struct wl_listener *listener, void *data)
|
||||
{
|
||||
MetaWaylandSeat *seat =
|
||||
wl_container_of (listener, seat, drag_icon_listener);
|
||||
|
||||
seat->drag_surface = NULL;
|
||||
}
|
||||
|
||||
static void
|
||||
data_device_start_drag (struct wl_client *client,
|
||||
struct wl_resource *resource,
|
||||
struct wl_resource *source_resource,
|
||||
struct wl_resource *origin_resource,
|
||||
struct wl_resource *icon_resource, guint32 serial)
|
||||
{
|
||||
MetaWaylandSeat *seat = wl_resource_get_user_data (resource);
|
||||
|
||||
/* FIXME: Check that client has implicit grab on the origin
|
||||
* surface that matches the given time. */
|
||||
|
||||
/* FIXME: Check that the data source type array isn't empty. */
|
||||
|
||||
seat->drag_grab.interface = &drag_grab_interface;
|
||||
|
||||
seat->drag_client = client;
|
||||
seat->drag_data_source = NULL;
|
||||
|
||||
if (source_resource)
|
||||
{
|
||||
seat->drag_data_source = wl_resource_get_user_data (source_resource);
|
||||
seat->drag_data_source_listener.notify = destroy_data_device_source;
|
||||
wl_resource_add_destroy_listener (source_resource,
|
||||
&seat->drag_data_source_listener);
|
||||
}
|
||||
|
||||
if (icon_resource)
|
||||
{
|
||||
seat->drag_surface = wl_resource_get_user_data (icon_resource);
|
||||
seat->drag_icon_listener.notify = destroy_data_device_icon;
|
||||
wl_resource_add_destroy_listener (icon_resource,
|
||||
&seat->drag_icon_listener);
|
||||
wl_signal_emit (&seat->drag_icon_signal, icon_resource);
|
||||
}
|
||||
|
||||
meta_wayland_pointer_set_focus (&seat->pointer, NULL,
|
||||
wl_fixed_from_int (0),
|
||||
wl_fixed_from_int (0));
|
||||
meta_wayland_pointer_start_grab (&seat->pointer, &seat->drag_grab);
|
||||
}
|
||||
|
||||
static void
|
||||
destroy_selection_data_source (struct wl_listener *listener, void *data)
|
||||
{
|
||||
MetaWaylandSeat *seat =
|
||||
wl_container_of (listener, seat, selection_data_source_listener);
|
||||
struct wl_resource *data_device;
|
||||
struct wl_resource *focus = NULL;
|
||||
|
||||
seat->selection_data_source = NULL;
|
||||
|
||||
focus = seat->keyboard.focus_resource;
|
||||
|
||||
if (focus)
|
||||
{
|
||||
data_device =
|
||||
wl_resource_find_for_client (&seat->drag_resource_list,
|
||||
wl_resource_get_client (focus));
|
||||
if (data_device)
|
||||
wl_data_device_send_selection (data_device, NULL);
|
||||
}
|
||||
|
||||
wl_signal_emit (&seat->selection_signal, seat);
|
||||
}
|
||||
|
||||
void
|
||||
meta_wayland_seat_set_selection (MetaWaylandSeat *seat,
|
||||
MetaWaylandDataSource *source,
|
||||
guint32 serial)
|
||||
{
|
||||
struct wl_resource *data_device, *offer;
|
||||
struct wl_resource *focus = NULL;
|
||||
|
||||
if (seat->selection_data_source &&
|
||||
seat->selection_serial - serial < UINT32_MAX / 2)
|
||||
return;
|
||||
|
||||
if (seat->selection_data_source)
|
||||
{
|
||||
seat->selection_data_source->cancel (seat->selection_data_source);
|
||||
wl_list_remove (&seat->selection_data_source_listener.link);
|
||||
seat->selection_data_source = NULL;
|
||||
}
|
||||
|
||||
seat->selection_data_source = source;
|
||||
seat->selection_serial = serial;
|
||||
|
||||
focus = seat->keyboard.focus_resource;
|
||||
|
||||
if (focus)
|
||||
{
|
||||
data_device =
|
||||
wl_resource_find_for_client (&seat->drag_resource_list,
|
||||
wl_resource_get_client (focus));
|
||||
if (data_device && source)
|
||||
{
|
||||
offer =
|
||||
meta_wayland_data_source_send_offer (seat->selection_data_source,
|
||||
data_device);
|
||||
wl_data_device_send_selection (data_device, offer);
|
||||
}
|
||||
else if (data_device)
|
||||
{
|
||||
wl_data_device_send_selection (data_device, NULL);
|
||||
}
|
||||
}
|
||||
|
||||
wl_signal_emit (&seat->selection_signal, seat);
|
||||
|
||||
if (source)
|
||||
{
|
||||
seat->selection_data_source_listener.notify =
|
||||
destroy_selection_data_source;
|
||||
wl_resource_add_destroy_listener (source->resource,
|
||||
&seat->selection_data_source_listener);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
data_device_set_selection (struct wl_client *client,
|
||||
struct wl_resource *resource,
|
||||
struct wl_resource *source_resource,
|
||||
guint32 serial)
|
||||
{
|
||||
if (!source_resource)
|
||||
return;
|
||||
|
||||
/* FIXME: Store serial and check against incoming serial here. */
|
||||
meta_wayland_seat_set_selection (wl_resource_get_user_data (resource),
|
||||
wl_resource_get_user_data (source_resource),
|
||||
serial);
|
||||
}
|
||||
|
||||
static const struct wl_data_device_interface data_device_interface = {
|
||||
data_device_start_drag,
|
||||
data_device_set_selection,
|
||||
};
|
||||
|
||||
static void
|
||||
destroy_data_source (struct wl_resource *resource)
|
||||
{
|
||||
MetaWaylandDataSource *source = wl_container_of (resource, source, resource);
|
||||
char **p;
|
||||
|
||||
wl_array_for_each (p, &source->mime_types) free (*p);
|
||||
|
||||
wl_array_release (&source->mime_types);
|
||||
}
|
||||
|
||||
static void
|
||||
client_source_accept (MetaWaylandDataSource *source,
|
||||
guint32 time, const char *mime_type)
|
||||
{
|
||||
wl_data_source_send_target (source->resource, mime_type);
|
||||
}
|
||||
|
||||
static void
|
||||
client_source_send (MetaWaylandDataSource *source,
|
||||
const char *mime_type, int32_t fd)
|
||||
{
|
||||
wl_data_source_send_send (source->resource, mime_type, fd);
|
||||
close (fd);
|
||||
}
|
||||
|
||||
static void
|
||||
client_source_cancel (MetaWaylandDataSource *source)
|
||||
{
|
||||
wl_data_source_send_cancelled (source->resource);
|
||||
}
|
||||
|
||||
static void
|
||||
create_data_source (struct wl_client *client,
|
||||
struct wl_resource *resource, guint32 id)
|
||||
{
|
||||
MetaWaylandDataSource *source;
|
||||
|
||||
source = malloc (sizeof *source);
|
||||
if (source == NULL)
|
||||
{
|
||||
wl_resource_post_no_memory (resource);
|
||||
return;
|
||||
}
|
||||
|
||||
source->resource = wl_client_add_object (client,
|
||||
&wl_data_source_interface,
|
||||
&data_source_interface,
|
||||
id,
|
||||
source);
|
||||
wl_resource_set_destructor (source->resource, destroy_data_source);
|
||||
|
||||
source->accept = client_source_accept;
|
||||
source->send = client_source_send;
|
||||
source->cancel = client_source_cancel;
|
||||
|
||||
wl_array_init (&source->mime_types);
|
||||
}
|
||||
|
||||
static void
|
||||
unbind_data_device (struct wl_resource *resource)
|
||||
{
|
||||
wl_list_remove (wl_resource_get_link (resource));
|
||||
}
|
||||
|
||||
static void
|
||||
get_data_device (struct wl_client *client,
|
||||
struct wl_resource *manager_resource,
|
||||
guint32 id, struct wl_resource *seat_resource)
|
||||
{
|
||||
MetaWaylandSeat *seat = wl_resource_get_user_data (seat_resource);
|
||||
struct wl_resource *resource;
|
||||
|
||||
resource = wl_client_add_object (client, &wl_data_device_interface,
|
||||
&data_device_interface, id, seat);
|
||||
|
||||
wl_list_insert (&seat->drag_resource_list, wl_resource_get_link (resource));
|
||||
wl_resource_set_destructor (resource, unbind_data_device);
|
||||
}
|
||||
|
||||
static const struct wl_data_device_manager_interface manager_interface = {
|
||||
create_data_source,
|
||||
get_data_device
|
||||
};
|
||||
|
||||
static void
|
||||
bind_manager (struct wl_client *client,
|
||||
void *data, guint32 version, guint32 id)
|
||||
{
|
||||
wl_client_add_object (client, &wl_data_device_manager_interface,
|
||||
&manager_interface, id, NULL);
|
||||
}
|
||||
|
||||
void
|
||||
meta_wayland_data_device_set_keyboard_focus (MetaWaylandSeat *seat)
|
||||
{
|
||||
struct wl_resource *data_device, *focus, *offer;
|
||||
MetaWaylandDataSource *source;
|
||||
|
||||
focus = seat->keyboard.focus_resource;
|
||||
if (!focus)
|
||||
return;
|
||||
|
||||
data_device = wl_resource_find_for_client (&seat->drag_resource_list,
|
||||
wl_resource_get_client (focus));
|
||||
if (!data_device)
|
||||
return;
|
||||
|
||||
source = seat->selection_data_source;
|
||||
if (source)
|
||||
{
|
||||
offer = meta_wayland_data_source_send_offer (source, data_device);
|
||||
wl_data_device_send_selection (data_device, offer);
|
||||
}
|
||||
}
|
||||
|
||||
int
|
||||
meta_wayland_data_device_manager_init (struct wl_display *display)
|
||||
{
|
||||
if (wl_display_add_global (display,
|
||||
&wl_data_device_manager_interface,
|
||||
NULL, bind_manager) == NULL)
|
||||
return -1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
42
src/wayland/meta-wayland-data-device.h
Normal file
42
src/wayland/meta-wayland-data-device.h
Normal file
@@ -0,0 +1,42 @@
|
||||
/*
|
||||
* Copyright © 2008 Kristian Høgsberg
|
||||
*
|
||||
* 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 __META_WAYLAND_DATA_DEVICE_H__
|
||||
#define __META_WAYLAND_DATA_DEVICE_H__
|
||||
|
||||
#include <wayland-server.h>
|
||||
|
||||
#include "meta-wayland-seat.h"
|
||||
|
||||
void
|
||||
meta_wayland_data_device_set_keyboard_focus (MetaWaylandSeat *seat);
|
||||
|
||||
int
|
||||
meta_wayland_data_device_manager_init (struct wl_display *display);
|
||||
|
||||
void
|
||||
meta_wayland_seat_set_selection (MetaWaylandSeat *seat,
|
||||
MetaWaylandDataSource *source,
|
||||
uint32_t serial);
|
||||
|
||||
|
||||
#endif /* __META_WAYLAND_DATA_DEVICE_H__ */
|
||||
675
src/wayland/meta-wayland-keyboard.c
Normal file
675
src/wayland/meta-wayland-keyboard.c
Normal file
@@ -0,0 +1,675 @@
|
||||
/*
|
||||
* Wayland Support
|
||||
*
|
||||
* Copyright (C) 2013 Intel Corporation
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
/*
|
||||
* Copyright © 2010-2011 Intel Corporation
|
||||
* Copyright © 2008-2011 Kristian Høgsberg
|
||||
* Copyright © 2012 Collabora, Ltd.
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
/* The file is based on src/input.c from Weston */
|
||||
|
||||
#define _GNU_SOURCE
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#include <glib.h>
|
||||
#include <string.h>
|
||||
#include <errno.h>
|
||||
#include <stdlib.h>
|
||||
#include <fcntl.h>
|
||||
#include <unistd.h>
|
||||
#include <sys/mman.h>
|
||||
#include <clutter/evdev/clutter-evdev.h>
|
||||
|
||||
#include "meta-wayland-private.h"
|
||||
|
||||
static MetaWaylandSeat *
|
||||
meta_wayland_keyboard_get_seat (MetaWaylandKeyboard *keyboard)
|
||||
{
|
||||
MetaWaylandSeat *seat = wl_container_of (keyboard, seat, keyboard);
|
||||
|
||||
return seat;
|
||||
}
|
||||
|
||||
static int
|
||||
create_anonymous_file (off_t size,
|
||||
GError **error)
|
||||
{
|
||||
static const char template[] = "mutter-shared-XXXXXX";
|
||||
char *path;
|
||||
int fd, flags;
|
||||
|
||||
fd = g_file_open_tmp (template, &path, error);
|
||||
|
||||
if (fd == -1)
|
||||
return -1;
|
||||
|
||||
unlink (path);
|
||||
g_free (path);
|
||||
|
||||
flags = fcntl (fd, F_GETFD);
|
||||
if (flags == -1)
|
||||
goto err;
|
||||
|
||||
if (fcntl (fd, F_SETFD, flags | FD_CLOEXEC) == -1)
|
||||
goto err;
|
||||
|
||||
if (ftruncate (fd, size) < 0)
|
||||
goto err;
|
||||
|
||||
return fd;
|
||||
|
||||
err:
|
||||
g_set_error_literal (error,
|
||||
G_FILE_ERROR,
|
||||
g_file_error_from_errno (errno),
|
||||
strerror (errno));
|
||||
close (fd);
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
static gboolean
|
||||
meta_wayland_xkb_info_new_keymap (MetaWaylandXkbInfo *xkb_info)
|
||||
{
|
||||
GError *error = NULL;
|
||||
char *keymap_str;
|
||||
|
||||
xkb_info->shift_mod =
|
||||
xkb_map_mod_get_index (xkb_info->keymap, XKB_MOD_NAME_SHIFT);
|
||||
xkb_info->caps_mod =
|
||||
xkb_map_mod_get_index (xkb_info->keymap, XKB_MOD_NAME_CAPS);
|
||||
xkb_info->ctrl_mod =
|
||||
xkb_map_mod_get_index (xkb_info->keymap, XKB_MOD_NAME_CTRL);
|
||||
xkb_info->alt_mod =
|
||||
xkb_map_mod_get_index (xkb_info->keymap, XKB_MOD_NAME_ALT);
|
||||
xkb_info->mod2_mod = xkb_map_mod_get_index (xkb_info->keymap, "Mod2");
|
||||
xkb_info->mod3_mod = xkb_map_mod_get_index (xkb_info->keymap, "Mod3");
|
||||
xkb_info->super_mod =
|
||||
xkb_map_mod_get_index (xkb_info->keymap, XKB_MOD_NAME_LOGO);
|
||||
xkb_info->mod5_mod = xkb_map_mod_get_index (xkb_info->keymap, "Mod5");
|
||||
|
||||
keymap_str = xkb_map_get_as_string (xkb_info->keymap);
|
||||
if (keymap_str == NULL)
|
||||
{
|
||||
g_warning ("failed to get string version of keymap\n");
|
||||
return FALSE;
|
||||
}
|
||||
xkb_info->keymap_size = strlen (keymap_str) + 1;
|
||||
|
||||
xkb_info->keymap_fd = create_anonymous_file (xkb_info->keymap_size, &error);
|
||||
if (xkb_info->keymap_fd < 0)
|
||||
{
|
||||
g_warning ("creating a keymap file for %lu bytes failed: %s\n",
|
||||
(unsigned long) xkb_info->keymap_size,
|
||||
error->message);
|
||||
g_clear_error (&error);
|
||||
goto err_keymap_str;
|
||||
}
|
||||
|
||||
xkb_info->keymap_area = mmap (NULL, xkb_info->keymap_size,
|
||||
PROT_READ | PROT_WRITE,
|
||||
MAP_SHARED, xkb_info->keymap_fd, 0);
|
||||
if (xkb_info->keymap_area == MAP_FAILED)
|
||||
{
|
||||
g_warning ("failed to mmap() %lu bytes\n",
|
||||
(unsigned long) xkb_info->keymap_size);
|
||||
goto err_dev_zero;
|
||||
}
|
||||
strcpy (xkb_info->keymap_area, keymap_str);
|
||||
free (keymap_str);
|
||||
|
||||
return TRUE;
|
||||
|
||||
err_dev_zero:
|
||||
close (xkb_info->keymap_fd);
|
||||
xkb_info->keymap_fd = -1;
|
||||
err_keymap_str:
|
||||
free (keymap_str);
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
static gboolean
|
||||
meta_wayland_keyboard_build_global_keymap (struct xkb_context *xkb_context,
|
||||
struct xkb_rule_names *xkb_names,
|
||||
MetaWaylandXkbInfo *xkb_info)
|
||||
{
|
||||
xkb_info->keymap = xkb_map_new_from_names (xkb_context,
|
||||
xkb_names,
|
||||
0 /* flags */);
|
||||
if (xkb_info->keymap == NULL)
|
||||
{
|
||||
g_warning ("failed to compile global XKB keymap\n"
|
||||
" tried rules %s, model %s, layout %s, variant %s, "
|
||||
"options %s\n",
|
||||
xkb_names->rules,
|
||||
xkb_names->model,
|
||||
xkb_names->layout,
|
||||
xkb_names->variant,
|
||||
xkb_names->options);
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
if (!meta_wayland_xkb_info_new_keymap (xkb_info))
|
||||
return FALSE;
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static void
|
||||
lose_keyboard_focus (struct wl_listener *listener, void *data)
|
||||
{
|
||||
MetaWaylandKeyboard *keyboard =
|
||||
wl_container_of (listener, keyboard, focus_listener);
|
||||
|
||||
keyboard->focus_resource = NULL;
|
||||
}
|
||||
|
||||
static void
|
||||
default_grab_key (MetaWaylandKeyboardGrab *grab,
|
||||
uint32_t time, uint32_t key, uint32_t state)
|
||||
{
|
||||
MetaWaylandKeyboard *keyboard = grab->keyboard;
|
||||
struct wl_resource *resource;
|
||||
uint32_t serial;
|
||||
|
||||
resource = keyboard->focus_resource;
|
||||
if (resource)
|
||||
{
|
||||
struct wl_client *client = wl_resource_get_client (resource);
|
||||
struct wl_display *display = wl_client_get_display (client);
|
||||
serial = wl_display_next_serial (display);
|
||||
wl_keyboard_send_key (resource, serial, time, key, state);
|
||||
}
|
||||
}
|
||||
|
||||
static struct wl_resource *
|
||||
find_resource_for_surface (struct wl_list *list, MetaWaylandSurface *surface)
|
||||
{
|
||||
struct wl_client *client;
|
||||
|
||||
if (!surface)
|
||||
return NULL;
|
||||
|
||||
if (!surface->resource)
|
||||
return NULL;
|
||||
|
||||
client = wl_resource_get_client (surface->resource);
|
||||
|
||||
return wl_resource_find_for_client (list, client);
|
||||
}
|
||||
|
||||
static void
|
||||
default_grab_modifiers (MetaWaylandKeyboardGrab *grab, uint32_t serial,
|
||||
uint32_t mods_depressed, uint32_t mods_latched,
|
||||
uint32_t mods_locked, uint32_t group)
|
||||
{
|
||||
MetaWaylandKeyboard *keyboard = grab->keyboard;
|
||||
MetaWaylandSeat *seat = meta_wayland_keyboard_get_seat (keyboard);
|
||||
MetaWaylandPointer *pointer = &seat->pointer;
|
||||
struct wl_resource *resource, *pr;
|
||||
|
||||
resource = keyboard->focus_resource;
|
||||
if (!resource)
|
||||
return;
|
||||
|
||||
wl_keyboard_send_modifiers (resource, serial, mods_depressed,
|
||||
mods_latched, mods_locked, group);
|
||||
|
||||
if (pointer && pointer->focus && pointer->focus != keyboard->focus)
|
||||
{
|
||||
pr = find_resource_for_surface (&keyboard->resource_list,
|
||||
pointer->focus);
|
||||
if (pr)
|
||||
{
|
||||
wl_keyboard_send_modifiers (pr, serial,
|
||||
mods_depressed,
|
||||
mods_latched,
|
||||
mods_locked,
|
||||
group);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static const MetaWaylandKeyboardGrabInterface
|
||||
default_keyboard_grab_interface = {
|
||||
default_grab_key,
|
||||
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,
|
||||
gboolean is_evdev)
|
||||
{
|
||||
ClutterDeviceManager *manager;
|
||||
|
||||
memset (keyboard, 0, sizeof *keyboard);
|
||||
|
||||
wl_list_init (&keyboard->resource_list);
|
||||
wl_array_init (&keyboard->keys);
|
||||
keyboard->focus_listener.notify = lose_keyboard_focus;
|
||||
keyboard->default_grab.interface = &default_keyboard_grab_interface;
|
||||
keyboard->default_grab.keyboard = keyboard;
|
||||
keyboard->grab = &keyboard->default_grab;
|
||||
wl_signal_init (&keyboard->focus_signal);
|
||||
|
||||
keyboard->display = display;
|
||||
|
||||
keyboard->xkb_context = xkb_context_new (0 /* flags */);
|
||||
|
||||
meta_wayland_keyboard_build_global_keymap (keyboard->xkb_context,
|
||||
&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;
|
||||
}
|
||||
|
||||
static void
|
||||
meta_wayland_xkb_info_destroy (MetaWaylandXkbInfo *xkb_info)
|
||||
{
|
||||
if (xkb_info->keymap)
|
||||
xkb_map_unref (xkb_info->keymap);
|
||||
|
||||
if (xkb_info->keymap_area)
|
||||
munmap (xkb_info->keymap_area, xkb_info->keymap_size);
|
||||
if (xkb_info->keymap_fd >= 0)
|
||||
close (xkb_info->keymap_fd);
|
||||
}
|
||||
|
||||
static void
|
||||
update_state_from_clutter (MetaWaylandKeyboard *keyboard,
|
||||
ClutterModifierType modifier_state)
|
||||
{
|
||||
uint32_t depressed_mods = 0;
|
||||
uint32_t locked_mods = 0;
|
||||
|
||||
if ((modifier_state & CLUTTER_SHIFT_MASK) &&
|
||||
keyboard->xkb_info.shift_mod != XKB_MOD_INVALID)
|
||||
depressed_mods |= (1 << keyboard->xkb_info.shift_mod);
|
||||
|
||||
if ((modifier_state & CLUTTER_LOCK_MASK) &&
|
||||
keyboard->xkb_info.caps_mod != XKB_MOD_INVALID)
|
||||
locked_mods |= (1 << keyboard->xkb_info.caps_mod);
|
||||
|
||||
if ((modifier_state & CLUTTER_CONTROL_MASK) &&
|
||||
keyboard->xkb_info.ctrl_mod != XKB_MOD_INVALID)
|
||||
depressed_mods |= (1 << keyboard->xkb_info.ctrl_mod);
|
||||
|
||||
if ((modifier_state & CLUTTER_MOD1_MASK) &&
|
||||
keyboard->xkb_info.alt_mod != XKB_MOD_INVALID)
|
||||
depressed_mods |= (1 << keyboard->xkb_info.alt_mod);
|
||||
|
||||
if ((modifier_state & CLUTTER_MOD2_MASK) &&
|
||||
keyboard->xkb_info.mod2_mod != XKB_MOD_INVALID)
|
||||
depressed_mods |= (1 << keyboard->xkb_info.mod2_mod);
|
||||
|
||||
if ((modifier_state & CLUTTER_MOD3_MASK) &&
|
||||
keyboard->xkb_info.mod3_mod != XKB_MOD_INVALID)
|
||||
depressed_mods |= (1 << keyboard->xkb_info.mod3_mod);
|
||||
|
||||
if ((modifier_state & CLUTTER_SUPER_MASK) &&
|
||||
keyboard->xkb_info.super_mod != XKB_MOD_INVALID)
|
||||
depressed_mods |= (1 << keyboard->xkb_info.super_mod);
|
||||
|
||||
if ((modifier_state & CLUTTER_MOD5_MASK) &&
|
||||
keyboard->xkb_info.mod5_mod != XKB_MOD_INVALID)
|
||||
depressed_mods |= (1 << keyboard->xkb_info.mod5_mod);
|
||||
|
||||
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,
|
||||
new_state.mods_depressed,
|
||||
new_state.mods_latched,
|
||||
new_state.mods_locked,
|
||||
new_state.group);
|
||||
}
|
||||
|
||||
void
|
||||
meta_wayland_keyboard_handle_event (MetaWaylandKeyboard *keyboard,
|
||||
const ClutterKeyEvent *event)
|
||||
{
|
||||
gboolean state = event->type == CLUTTER_KEY_PRESS;
|
||||
guint evdev_code;
|
||||
uint32_t serial;
|
||||
|
||||
/* We can't do anything with the event if we can't get an evdev
|
||||
keycode for it */
|
||||
if (event->device == NULL ||
|
||||
!clutter_input_device_keycode_to_evdev (event->device,
|
||||
event->hardware_keycode,
|
||||
&evdev_code))
|
||||
return;
|
||||
|
||||
/* We want to ignore events that are sent because of auto-repeat. In
|
||||
the Clutter event stream these appear as a single key press
|
||||
event. We can detect that because the key will already have been
|
||||
pressed */
|
||||
if (state)
|
||||
{
|
||||
uint32_t *end = (void *) ((char *) keyboard->keys.data +
|
||||
keyboard->keys.size);
|
||||
uint32_t *k;
|
||||
|
||||
/* Ignore the event if the key is already down */
|
||||
for (k = keyboard->keys.data; k < end; k++)
|
||||
if (*k == evdev_code)
|
||||
return;
|
||||
|
||||
/* Otherwise add the key to the list of pressed keys */
|
||||
k = wl_array_add (&keyboard->keys, sizeof (*k));
|
||||
*k = evdev_code;
|
||||
}
|
||||
else
|
||||
{
|
||||
uint32_t *end = (void *) ((char *) keyboard->keys.data +
|
||||
keyboard->keys.size);
|
||||
uint32_t *k;
|
||||
|
||||
/* Remove the key from the array */
|
||||
for (k = keyboard->keys.data; k < end; k++)
|
||||
if (*k == evdev_code)
|
||||
{
|
||||
*k = *(end - 1);
|
||||
keyboard->keys.size -= sizeof (*k);
|
||||
|
||||
goto found;
|
||||
}
|
||||
|
||||
g_warning ("unexpected key release event for key 0x%x", evdev_code);
|
||||
|
||||
found:
|
||||
(void) 0;
|
||||
}
|
||||
|
||||
serial = wl_display_next_serial (keyboard->display);
|
||||
|
||||
set_modifiers (keyboard, serial, event->modifier_state);
|
||||
|
||||
keyboard->grab->interface->key (keyboard->grab,
|
||||
event->time,
|
||||
evdev_code,
|
||||
state);
|
||||
}
|
||||
|
||||
void
|
||||
meta_wayland_keyboard_set_focus (MetaWaylandKeyboard *keyboard,
|
||||
MetaWaylandSurface *surface)
|
||||
{
|
||||
struct wl_resource *resource;
|
||||
uint32_t serial;
|
||||
|
||||
if (keyboard->focus_resource && keyboard->focus != surface)
|
||||
{
|
||||
struct wl_display *display;
|
||||
struct wl_client *client;
|
||||
|
||||
resource = keyboard->focus_resource;
|
||||
client = wl_resource_get_client (resource);
|
||||
display = wl_client_get_display (client);
|
||||
serial = wl_display_next_serial (display);
|
||||
wl_keyboard_send_leave (resource, serial, keyboard->focus->resource);
|
||||
wl_list_remove (&keyboard->focus_listener.link);
|
||||
}
|
||||
|
||||
resource = find_resource_for_surface (&keyboard->resource_list, surface);
|
||||
if (resource &&
|
||||
(keyboard->focus != surface || keyboard->focus_resource != resource))
|
||||
{
|
||||
struct wl_client *client = wl_resource_get_client (resource);
|
||||
struct wl_display *display;
|
||||
|
||||
display = wl_client_get_display (client);
|
||||
serial = wl_display_next_serial (display);
|
||||
|
||||
/* 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;
|
||||
}
|
||||
|
||||
keyboard->focus_resource = resource;
|
||||
keyboard->focus = surface;
|
||||
wl_signal_emit (&keyboard->focus_signal, keyboard);
|
||||
}
|
||||
|
||||
void
|
||||
meta_wayland_keyboard_start_grab (MetaWaylandKeyboard *keyboard,
|
||||
MetaWaylandKeyboardGrab *grab)
|
||||
{
|
||||
keyboard->grab = grab;
|
||||
grab->keyboard = keyboard;
|
||||
|
||||
/* XXX focus? */
|
||||
}
|
||||
|
||||
void
|
||||
meta_wayland_keyboard_end_grab (MetaWaylandKeyboard *keyboard)
|
||||
{
|
||||
keyboard->grab = &keyboard->default_grab;
|
||||
}
|
||||
|
||||
void
|
||||
meta_wayland_keyboard_release (MetaWaylandKeyboard *keyboard)
|
||||
{
|
||||
g_free ((char *) keyboard->xkb_names.rules);
|
||||
g_free ((char *) keyboard->xkb_names.model);
|
||||
g_free ((char *) keyboard->xkb_names.layout);
|
||||
g_free ((char *) keyboard->xkb_names.variant);
|
||||
g_free ((char *) keyboard->xkb_names.options);
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
154
src/wayland/meta-wayland-keyboard.h
Normal file
154
src/wayland/meta-wayland-keyboard.h
Normal file
@@ -0,0 +1,154 @@
|
||||
/*
|
||||
* Wayland Support
|
||||
*
|
||||
* Copyright (C) 2013 Intel Corporation
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
/*
|
||||
* Copyright © 2008-2011 Kristian Høgsberg
|
||||
* Copyright © 2012 Collabora, Ltd.
|
||||
*
|
||||
* 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 __META_WAYLAND_KEYBOARD_H__
|
||||
#define __META_WAYLAND_KEYBOARD_H__
|
||||
|
||||
#include <clutter/clutter.h>
|
||||
#include <wayland-server.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,
|
||||
gboolean is_evdev);
|
||||
|
||||
void
|
||||
meta_wayland_keyboard_handle_event (MetaWaylandKeyboard *keyboard,
|
||||
const ClutterKeyEvent *event);
|
||||
|
||||
void
|
||||
meta_wayland_keyboard_set_focus (MetaWaylandKeyboard *keyboard,
|
||||
MetaWaylandSurface *surface);
|
||||
|
||||
void
|
||||
meta_wayland_keyboard_start_grab (MetaWaylandKeyboard *device,
|
||||
MetaWaylandKeyboardGrab *grab);
|
||||
|
||||
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);
|
||||
|
||||
#endif /* __META_WAYLAND_KEYBOARD_H__ */
|
||||
345
src/wayland/meta-wayland-pointer.c
Normal file
345
src/wayland/meta-wayland-pointer.c
Normal file
@@ -0,0 +1,345 @@
|
||||
/*
|
||||
* Wayland Support
|
||||
*
|
||||
* Copyright (C) 2013 Intel Corporation
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library 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
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
/*
|
||||
* Copyright © 2008 Kristian Høgsberg
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
/* The file is based on src/input.c from Weston */
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#include "meta-wayland-pointer.h"
|
||||
#include "meta-wayland-private.h"
|
||||
|
||||
#include <string.h>
|
||||
|
||||
static MetaWaylandSeat *
|
||||
meta_wayland_pointer_get_seat (MetaWaylandPointer *pointer)
|
||||
{
|
||||
MetaWaylandSeat *seat = wl_container_of (pointer, seat, pointer);
|
||||
|
||||
return seat;
|
||||
}
|
||||
|
||||
static void
|
||||
lose_pointer_focus (struct wl_listener *listener, void *data)
|
||||
{
|
||||
MetaWaylandPointer *pointer =
|
||||
wl_container_of (listener, pointer, focus_listener);
|
||||
|
||||
pointer->focus_resource = NULL;
|
||||
pointer->focus = NULL;
|
||||
}
|
||||
|
||||
static void
|
||||
default_grab_focus (MetaWaylandPointerGrab *grab,
|
||||
MetaWaylandSurface *surface,
|
||||
wl_fixed_t x,
|
||||
wl_fixed_t y)
|
||||
{
|
||||
MetaWaylandPointer *pointer = grab->pointer;
|
||||
|
||||
if (pointer->button_count > 0)
|
||||
return;
|
||||
|
||||
meta_wayland_pointer_set_focus (pointer, surface, x, y);
|
||||
}
|
||||
|
||||
static void
|
||||
default_grab_motion (MetaWaylandPointerGrab *grab,
|
||||
uint32_t time, wl_fixed_t x, wl_fixed_t y)
|
||||
{
|
||||
struct wl_resource *resource;
|
||||
|
||||
resource = grab->pointer->focus_resource;
|
||||
if (resource)
|
||||
wl_pointer_send_motion (resource, time, x, y);
|
||||
}
|
||||
|
||||
static void
|
||||
default_grab_button (MetaWaylandPointerGrab *grab,
|
||||
uint32_t time, uint32_t button, uint32_t state_w)
|
||||
{
|
||||
MetaWaylandPointer *pointer = grab->pointer;
|
||||
struct wl_resource *resource;
|
||||
uint32_t serial;
|
||||
enum wl_pointer_button_state state = state_w;
|
||||
|
||||
resource = pointer->focus_resource;
|
||||
if (resource)
|
||||
{
|
||||
struct wl_client *client = wl_resource_get_client (resource);
|
||||
struct wl_display *display = wl_client_get_display (client);
|
||||
serial = wl_display_next_serial (display);
|
||||
wl_pointer_send_button (resource, serial, time, button, state_w);
|
||||
}
|
||||
|
||||
if (pointer->button_count == 0 && state == WL_POINTER_BUTTON_STATE_RELEASED)
|
||||
meta_wayland_pointer_set_focus (pointer, pointer->current,
|
||||
pointer->current_x, pointer->current_y);
|
||||
}
|
||||
|
||||
static const MetaWaylandPointerGrabInterface default_pointer_grab_interface = {
|
||||
default_grab_focus,
|
||||
default_grab_motion,
|
||||
default_grab_button
|
||||
};
|
||||
|
||||
void
|
||||
meta_wayland_pointer_init (MetaWaylandPointer *pointer)
|
||||
{
|
||||
memset (pointer, 0, sizeof *pointer);
|
||||
wl_list_init (&pointer->resource_list);
|
||||
pointer->focus_listener.notify = lose_pointer_focus;
|
||||
pointer->default_grab.interface = &default_pointer_grab_interface;
|
||||
pointer->default_grab.pointer = pointer;
|
||||
pointer->grab = &pointer->default_grab;
|
||||
wl_signal_init (&pointer->focus_signal);
|
||||
|
||||
/* FIXME: Pick better co-ords. */
|
||||
pointer->x = wl_fixed_from_int (100);
|
||||
pointer->y = wl_fixed_from_int (100);
|
||||
}
|
||||
|
||||
void
|
||||
meta_wayland_pointer_release (MetaWaylandPointer *pointer)
|
||||
{
|
||||
/* XXX: What about pointer->resource_list? */
|
||||
if (pointer->focus_resource)
|
||||
wl_list_remove (&pointer->focus_listener.link);
|
||||
|
||||
pointer->focus = NULL;
|
||||
pointer->focus_resource = NULL;
|
||||
}
|
||||
|
||||
static struct wl_resource *
|
||||
find_resource_for_surface (struct wl_list *list, MetaWaylandSurface *surface)
|
||||
{
|
||||
struct wl_client *client;
|
||||
|
||||
if (!surface)
|
||||
return NULL;
|
||||
|
||||
g_assert (surface->resource);
|
||||
client = wl_resource_get_client (surface->resource);
|
||||
|
||||
return wl_resource_find_for_client (list, client);
|
||||
}
|
||||
|
||||
void
|
||||
meta_wayland_pointer_set_focus (MetaWaylandPointer *pointer,
|
||||
MetaWaylandSurface *surface,
|
||||
wl_fixed_t sx, wl_fixed_t sy)
|
||||
{
|
||||
MetaWaylandSeat *seat = meta_wayland_pointer_get_seat (pointer);
|
||||
MetaWaylandKeyboard *kbd = &seat->keyboard;
|
||||
struct wl_resource *resource, *kr;
|
||||
uint32_t serial;
|
||||
|
||||
resource = pointer->focus_resource;
|
||||
if (resource && pointer->focus != surface)
|
||||
{
|
||||
struct wl_client *client = wl_resource_get_client (resource);
|
||||
struct wl_display *display = wl_client_get_display (client);
|
||||
serial = wl_display_next_serial (display);
|
||||
wl_pointer_send_leave (resource, serial, pointer->focus->resource);
|
||||
wl_list_remove (&pointer->focus_listener.link);
|
||||
}
|
||||
|
||||
resource = find_resource_for_surface (&pointer->resource_list, surface);
|
||||
if (resource &&
|
||||
(pointer->focus != surface || pointer->focus_resource != resource))
|
||||
{
|
||||
struct wl_client *client = wl_resource_get_client (resource);
|
||||
struct wl_display *display = wl_client_get_display (client);
|
||||
serial = wl_display_next_serial (display);
|
||||
if (kbd)
|
||||
{
|
||||
kr = find_resource_for_surface (&kbd->resource_list, surface);
|
||||
if (kr)
|
||||
{
|
||||
wl_keyboard_send_modifiers (kr,
|
||||
serial,
|
||||
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);
|
||||
wl_resource_add_destroy_listener (resource, &pointer->focus_listener);
|
||||
pointer->focus_serial = serial;
|
||||
}
|
||||
|
||||
pointer->focus_resource = resource;
|
||||
pointer->focus = surface;
|
||||
pointer->default_grab.focus = surface;
|
||||
wl_signal_emit (&pointer->focus_signal, pointer);
|
||||
}
|
||||
|
||||
void
|
||||
meta_wayland_pointer_start_grab (MetaWaylandPointer *pointer,
|
||||
MetaWaylandPointerGrab *grab)
|
||||
{
|
||||
const MetaWaylandPointerGrabInterface *interface;
|
||||
|
||||
pointer->grab = grab;
|
||||
interface = pointer->grab->interface;
|
||||
grab->pointer = pointer;
|
||||
|
||||
if (pointer->current)
|
||||
interface->focus (pointer->grab, pointer->current,
|
||||
pointer->current_x, pointer->current_y);
|
||||
}
|
||||
|
||||
void
|
||||
meta_wayland_pointer_end_grab (MetaWaylandPointer *pointer)
|
||||
{
|
||||
const MetaWaylandPointerGrabInterface *interface;
|
||||
|
||||
pointer->grab = &pointer->default_grab;
|
||||
interface = pointer->grab->interface;
|
||||
interface->focus (pointer->grab, pointer->current,
|
||||
pointer->current_x, pointer->current_y);
|
||||
}
|
||||
|
||||
static void
|
||||
current_surface_destroy (struct wl_listener *listener, void *data)
|
||||
{
|
||||
MetaWaylandPointer *pointer =
|
||||
wl_container_of (listener, pointer, current_listener);
|
||||
|
||||
pointer->current = NULL;
|
||||
}
|
||||
|
||||
void
|
||||
meta_wayland_pointer_set_current (MetaWaylandPointer *pointer,
|
||||
MetaWaylandSurface *surface)
|
||||
{
|
||||
if (pointer->current)
|
||||
wl_list_remove (&pointer->current_listener.link);
|
||||
|
||||
pointer->current = surface;
|
||||
|
||||
if (!surface)
|
||||
return;
|
||||
|
||||
wl_resource_add_destroy_listener (surface->resource,
|
||||
&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);
|
||||
}
|
||||
}
|
||||
102
src/wayland/meta-wayland-pointer.h
Normal file
102
src/wayland/meta-wayland-pointer.h
Normal file
@@ -0,0 +1,102 @@
|
||||
/*
|
||||
* Wayland Support
|
||||
*
|
||||
* Copyright (C) 2013 Intel Corporation
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library 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
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef __META_WAYLAND_POINTER_H__
|
||||
#define __META_WAYLAND_POINTER_H__
|
||||
|
||||
#include <wayland-server.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);
|
||||
|
||||
void
|
||||
meta_wayland_pointer_release (MetaWaylandPointer *pointer);
|
||||
|
||||
void
|
||||
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);
|
||||
|
||||
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);
|
||||
|
||||
#endif /* __META_WAYLAND_POINTER_H__ */
|
||||
114
src/wayland/meta-wayland-private.h
Normal file
114
src/wayland/meta-wayland-private.h
Normal file
@@ -0,0 +1,114 @@
|
||||
/*
|
||||
* Copyright (C) 2012 Intel Corporation
|
||||
*
|
||||
* 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_PRIVATE_H
|
||||
#define META_WAYLAND_PRIVATE_H
|
||||
|
||||
#include <wayland-server.h>
|
||||
#include <xkbcommon/xkbcommon.h>
|
||||
#include <clutter/clutter.h>
|
||||
|
||||
#include <glib.h>
|
||||
#include <cairo.h>
|
||||
|
||||
#include "window-private.h"
|
||||
#include "meta-weston-launch.h"
|
||||
#include <meta/meta-cursor-tracker.h>
|
||||
|
||||
#include "meta-wayland-types.h"
|
||||
#include "meta-wayland-surface.h"
|
||||
#include "meta-wayland-seat.h"
|
||||
|
||||
typedef struct
|
||||
{
|
||||
struct wl_resource *resource;
|
||||
cairo_region_t *region;
|
||||
} MetaWaylandRegion;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
GSource source;
|
||||
GPollFD pfd;
|
||||
struct wl_display *display;
|
||||
} WaylandEventSource;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
struct wl_list link;
|
||||
|
||||
/* Pointer back to the compositor */
|
||||
MetaWaylandCompositor *compositor;
|
||||
|
||||
struct wl_resource *resource;
|
||||
} MetaWaylandFrameCallback;
|
||||
|
||||
struct _MetaWaylandCompositor
|
||||
{
|
||||
struct wl_display *wayland_display;
|
||||
struct wl_event_loop *wayland_loop;
|
||||
GMainLoop *init_loop;
|
||||
ClutterActor *stage;
|
||||
GHashTable *outputs;
|
||||
GSource *wayland_event_source;
|
||||
GList *surfaces;
|
||||
struct wl_list frame_callbacks;
|
||||
|
||||
int xwayland_display_index;
|
||||
char *xwayland_lockfile;
|
||||
int xwayland_abstract_fd;
|
||||
int xwayland_unix_fd;
|
||||
pid_t xwayland_pid;
|
||||
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
|
||||
synthesizing XEvents for Mutter */
|
||||
MetaWaylandSurface *implicit_grab_surface;
|
||||
/* Button that was pressed to initiate an implicit grab. The
|
||||
implicit grab will only be released when this button is
|
||||
released */
|
||||
guint32 implicit_grab_button;
|
||||
};
|
||||
|
||||
void meta_wayland_init (void);
|
||||
void meta_wayland_finalize (void);
|
||||
|
||||
/* We maintain a singleton MetaWaylandCompositor which can be got at via this
|
||||
* API after meta_wayland_init() has been called. */
|
||||
MetaWaylandCompositor *meta_wayland_compositor_get_default (void);
|
||||
|
||||
void meta_wayland_compositor_repick (MetaWaylandCompositor *compositor);
|
||||
|
||||
void meta_wayland_compositor_set_input_focus (MetaWaylandCompositor *compositor,
|
||||
MetaWindow *window);
|
||||
|
||||
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 */
|
||||
553
src/wayland/meta-wayland-seat.c
Normal file
553
src/wayland/meta-wayland-seat.c
Normal file
@@ -0,0 +1,553 @@
|
||||
/*
|
||||
* Wayland Support
|
||||
*
|
||||
* Copyright (C) 2013 Intel Corporation
|
||||
*
|
||||
* 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 <cogl/cogl-wayland-server.h>
|
||||
#include <clutter/clutter.h>
|
||||
#include <clutter/wayland/clutter-wayland-compositor.h>
|
||||
#include <clutter/wayland/clutter-wayland-surface.h>
|
||||
#include <linux/input.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include "meta-wayland-seat.h"
|
||||
#include "meta-wayland-private.h"
|
||||
#include "meta-wayland-keyboard.h"
|
||||
#include "meta-wayland-pointer.h"
|
||||
#include "meta-wayland-data-device.h"
|
||||
#include "meta-window-actor-private.h"
|
||||
#include "meta/meta-shaped-texture.h"
|
||||
#include "meta-shaped-texture-private.h"
|
||||
#include "meta-wayland-stage.h"
|
||||
#include "meta-cursor-tracker-private.h"
|
||||
|
||||
#define DEFAULT_AXIS_STEP_DISTANCE wl_fixed_from_int (10)
|
||||
|
||||
static void
|
||||
unbind_resource (struct wl_resource *resource)
|
||||
{
|
||||
wl_list_remove (wl_resource_get_link (resource));
|
||||
}
|
||||
|
||||
static void
|
||||
transform_stage_point_fixed (MetaWaylandSurface *surface,
|
||||
wl_fixed_t x,
|
||||
wl_fixed_t y,
|
||||
wl_fixed_t *sx,
|
||||
wl_fixed_t *sy)
|
||||
{
|
||||
float xf = 0.0f, yf = 0.0f;
|
||||
|
||||
if (surface->window)
|
||||
{
|
||||
ClutterActor *actor =
|
||||
CLUTTER_ACTOR (meta_window_get_compositor_private (surface->window));
|
||||
|
||||
if (actor)
|
||||
clutter_actor_transform_stage_point (actor,
|
||||
wl_fixed_to_double (x),
|
||||
wl_fixed_to_double (y),
|
||||
&xf, &yf);
|
||||
}
|
||||
|
||||
*sx = wl_fixed_from_double (xf);
|
||||
*sy = wl_fixed_from_double (yf);
|
||||
}
|
||||
|
||||
static void
|
||||
pointer_unmap_sprite (MetaWaylandSeat *seat)
|
||||
{
|
||||
if (seat->cursor_tracker)
|
||||
{
|
||||
meta_cursor_tracker_set_sprite (seat->cursor_tracker,
|
||||
NULL, 0, 0);
|
||||
|
||||
if (seat->current_stage)
|
||||
meta_cursor_tracker_queue_redraw (seat->cursor_tracker,
|
||||
CLUTTER_ACTOR (seat->current_stage));
|
||||
}
|
||||
|
||||
if (seat->sprite)
|
||||
{
|
||||
wl_list_remove (&seat->sprite_destroy_listener.link);
|
||||
seat->sprite = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
meta_wayland_seat_update_sprite (MetaWaylandSeat *seat)
|
||||
{
|
||||
ClutterBackend *backend;
|
||||
CoglContext *context;
|
||||
struct wl_resource *buffer;
|
||||
CoglTexture2D *texture;
|
||||
|
||||
if (seat->cursor_tracker == NULL)
|
||||
return;
|
||||
|
||||
backend = clutter_get_default_backend ();
|
||||
context = clutter_backend_get_cogl_context (backend);
|
||||
buffer = seat->sprite->buffer_ref.buffer->resource;
|
||||
texture = cogl_wayland_texture_2d_new_from_buffer (context, buffer, NULL);
|
||||
|
||||
meta_cursor_tracker_set_sprite (seat->cursor_tracker,
|
||||
texture,
|
||||
seat->hotspot_x,
|
||||
seat->hotspot_y);
|
||||
|
||||
if (seat->current_stage)
|
||||
meta_cursor_tracker_queue_redraw (seat->cursor_tracker,
|
||||
CLUTTER_ACTOR (seat->current_stage));
|
||||
|
||||
cogl_object_unref (texture);
|
||||
}
|
||||
|
||||
static void
|
||||
pointer_set_cursor (struct wl_client *client,
|
||||
struct wl_resource *resource,
|
||||
uint32_t serial,
|
||||
struct wl_resource *surface_resource,
|
||||
int32_t x, int32_t y)
|
||||
{
|
||||
MetaWaylandSeat *seat = wl_resource_get_user_data (resource);
|
||||
MetaWaylandSurface *surface;
|
||||
|
||||
surface = (surface_resource ?
|
||||
wl_resource_get_user_data (surface_resource) :
|
||||
NULL);
|
||||
|
||||
if (seat->pointer.focus == NULL)
|
||||
return;
|
||||
if (wl_resource_get_client (seat->pointer.focus->resource) != client)
|
||||
return;
|
||||
if (seat->pointer.focus_serial - serial > G_MAXUINT32 / 2)
|
||||
return;
|
||||
|
||||
seat->hotspot_x = x;
|
||||
seat->hotspot_y = y;
|
||||
|
||||
if (seat->sprite != surface)
|
||||
{
|
||||
pointer_unmap_sprite (seat);
|
||||
|
||||
if (!surface)
|
||||
return;
|
||||
|
||||
wl_resource_add_destroy_listener (surface->resource,
|
||||
&seat->sprite_destroy_listener);
|
||||
|
||||
seat->sprite = surface;
|
||||
|
||||
if (seat->sprite->buffer_ref.buffer)
|
||||
meta_wayland_seat_update_sprite (seat);
|
||||
}
|
||||
}
|
||||
|
||||
static const struct wl_pointer_interface
|
||||
pointer_interface =
|
||||
{
|
||||
pointer_set_cursor
|
||||
};
|
||||
|
||||
static void
|
||||
seat_get_pointer (struct wl_client *client,
|
||||
struct wl_resource *resource,
|
||||
uint32_t id)
|
||||
{
|
||||
MetaWaylandSeat *seat = wl_resource_get_user_data (resource);
|
||||
struct wl_resource *cr;
|
||||
|
||||
cr = wl_client_add_object (client, &wl_pointer_interface,
|
||||
&pointer_interface, id, seat);
|
||||
wl_list_insert (&seat->pointer.resource_list, wl_resource_get_link (cr));
|
||||
wl_resource_set_destructor (cr, unbind_resource);
|
||||
|
||||
if (seat->pointer.focus &&
|
||||
wl_resource_get_client (seat->pointer.focus->resource) == client)
|
||||
{
|
||||
MetaWaylandSurface *surface;
|
||||
wl_fixed_t sx, sy;
|
||||
|
||||
surface = (MetaWaylandSurface *) seat->pointer.focus;
|
||||
transform_stage_point_fixed (surface,
|
||||
seat->pointer.x,
|
||||
seat->pointer.y,
|
||||
&sx, &sy);
|
||||
meta_wayland_pointer_set_focus (&seat->pointer,
|
||||
seat->pointer.focus,
|
||||
sx, sy);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
seat_get_keyboard (struct wl_client *client,
|
||||
struct wl_resource *resource,
|
||||
uint32_t id)
|
||||
{
|
||||
MetaWaylandSeat *seat = wl_resource_get_user_data (resource);
|
||||
struct wl_resource *cr;
|
||||
|
||||
cr = wl_client_add_object (client, &wl_keyboard_interface, NULL, id, seat);
|
||||
wl_list_insert (&seat->keyboard.resource_list, wl_resource_get_link (cr));
|
||||
wl_resource_set_destructor (cr, unbind_resource);
|
||||
|
||||
wl_keyboard_send_keymap (cr,
|
||||
WL_KEYBOARD_KEYMAP_FORMAT_XKB_V1,
|
||||
seat->keyboard.xkb_info.keymap_fd,
|
||||
seat->keyboard.xkb_info.keymap_size);
|
||||
|
||||
if (seat->keyboard.focus &&
|
||||
wl_resource_get_client (seat->keyboard.focus->resource) == client)
|
||||
{
|
||||
meta_wayland_keyboard_set_focus (&seat->keyboard,
|
||||
seat->keyboard.focus);
|
||||
meta_wayland_data_device_set_keyboard_focus (seat);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
seat_get_touch (struct wl_client *client,
|
||||
struct wl_resource *resource,
|
||||
uint32_t id)
|
||||
{
|
||||
/* Touch not supported */
|
||||
}
|
||||
|
||||
static const struct wl_seat_interface
|
||||
seat_interface =
|
||||
{
|
||||
seat_get_pointer,
|
||||
seat_get_keyboard,
|
||||
seat_get_touch
|
||||
};
|
||||
|
||||
static void
|
||||
bind_seat (struct wl_client *client,
|
||||
void *data,
|
||||
guint32 version,
|
||||
guint32 id)
|
||||
{
|
||||
MetaWaylandSeat *seat = data;
|
||||
struct wl_resource *resource;
|
||||
|
||||
resource = wl_client_add_object (client,
|
||||
&wl_seat_interface,
|
||||
&seat_interface,
|
||||
id,
|
||||
data);
|
||||
wl_list_insert (&seat->base_resource_list, wl_resource_get_link (resource));
|
||||
wl_resource_set_destructor (resource, unbind_resource);
|
||||
|
||||
wl_seat_send_capabilities (resource,
|
||||
WL_SEAT_CAPABILITY_POINTER |
|
||||
WL_SEAT_CAPABILITY_KEYBOARD);
|
||||
}
|
||||
|
||||
static void
|
||||
pointer_handle_sprite_destroy (struct wl_listener *listener, void *data)
|
||||
{
|
||||
MetaWaylandSeat *seat =
|
||||
wl_container_of (listener, seat, sprite_destroy_listener);
|
||||
|
||||
pointer_unmap_sprite (seat);
|
||||
}
|
||||
|
||||
MetaWaylandSeat *
|
||||
meta_wayland_seat_new (struct wl_display *display,
|
||||
gboolean is_native)
|
||||
{
|
||||
MetaWaylandSeat *seat = g_new0 (MetaWaylandSeat, 1);
|
||||
|
||||
wl_signal_init (&seat->destroy_signal);
|
||||
|
||||
seat->selection_data_source = NULL;
|
||||
wl_list_init (&seat->base_resource_list);
|
||||
wl_signal_init (&seat->selection_signal);
|
||||
wl_list_init (&seat->drag_resource_list);
|
||||
wl_signal_init (&seat->drag_icon_signal);
|
||||
|
||||
meta_wayland_pointer_init (&seat->pointer);
|
||||
|
||||
meta_wayland_keyboard_init (&seat->keyboard, display, is_native);
|
||||
|
||||
seat->display = display;
|
||||
|
||||
seat->current_stage = 0;
|
||||
|
||||
seat->sprite = NULL;
|
||||
seat->sprite_destroy_listener.notify = pointer_handle_sprite_destroy;
|
||||
seat->hotspot_x = 16;
|
||||
seat->hotspot_y = 16;
|
||||
|
||||
wl_display_add_global (display, &wl_seat_interface, seat, bind_seat);
|
||||
|
||||
return seat;
|
||||
}
|
||||
|
||||
static void
|
||||
notify_motion (MetaWaylandSeat *seat,
|
||||
const ClutterEvent *event)
|
||||
{
|
||||
MetaWaylandPointer *pointer = &seat->pointer;
|
||||
float x, y;
|
||||
|
||||
clutter_event_get_coords (event, &x, &y);
|
||||
pointer->x = wl_fixed_from_double (x);
|
||||
pointer->y = wl_fixed_from_double (y);
|
||||
|
||||
meta_wayland_seat_repick (seat,
|
||||
clutter_event_get_time (event),
|
||||
clutter_event_get_source (event));
|
||||
|
||||
pointer->grab->interface->motion (pointer->grab,
|
||||
clutter_event_get_time (event),
|
||||
pointer->grab->x,
|
||||
pointer->grab->y);
|
||||
}
|
||||
|
||||
static void
|
||||
handle_motion_event (MetaWaylandSeat *seat,
|
||||
const ClutterMotionEvent *event)
|
||||
{
|
||||
notify_motion (seat, (const ClutterEvent *) event);
|
||||
}
|
||||
|
||||
static void
|
||||
handle_button_event (MetaWaylandSeat *seat,
|
||||
const ClutterButtonEvent *event)
|
||||
{
|
||||
MetaWaylandPointer *pointer = &seat->pointer;
|
||||
gboolean state = event->type == CLUTTER_BUTTON_PRESS;
|
||||
uint32_t button;
|
||||
|
||||
notify_motion (seat, (const ClutterEvent *) event);
|
||||
|
||||
switch (event->button)
|
||||
{
|
||||
/* The evdev input right and middle button numbers are swapped
|
||||
relative to how Clutter numbers them */
|
||||
case 2:
|
||||
button = BTN_MIDDLE;
|
||||
break;
|
||||
|
||||
case 3:
|
||||
button = BTN_RIGHT;
|
||||
break;
|
||||
|
||||
default:
|
||||
button = event->button + BTN_LEFT - 1;
|
||||
break;
|
||||
}
|
||||
|
||||
if (state)
|
||||
{
|
||||
if (pointer->button_count == 0)
|
||||
{
|
||||
MetaWaylandSurface *surface = pointer->current;
|
||||
|
||||
pointer->grab_button = button;
|
||||
pointer->grab_time = event->time;
|
||||
pointer->grab_x = pointer->x;
|
||||
pointer->grab_y = pointer->y;
|
||||
|
||||
if (button == BTN_LEFT &&
|
||||
surface &&
|
||||
surface->window &&
|
||||
surface->window->client_type == META_WINDOW_CLIENT_TYPE_WAYLAND)
|
||||
{
|
||||
meta_window_raise (surface->window);
|
||||
}
|
||||
}
|
||||
|
||||
pointer->button_count++;
|
||||
}
|
||||
else
|
||||
pointer->button_count--;
|
||||
|
||||
pointer->grab->interface->button (pointer->grab, event->time, button, state);
|
||||
|
||||
if (pointer->button_count == 1)
|
||||
pointer->grab_serial = wl_display_get_serial (seat->display);
|
||||
}
|
||||
|
||||
static void
|
||||
handle_scroll_event (MetaWaylandSeat *seat,
|
||||
const ClutterScrollEvent *event)
|
||||
{
|
||||
enum wl_pointer_axis axis;
|
||||
wl_fixed_t value;
|
||||
|
||||
notify_motion (seat, (const ClutterEvent *) event);
|
||||
|
||||
switch (event->direction)
|
||||
{
|
||||
case CLUTTER_SCROLL_UP:
|
||||
axis = WL_POINTER_AXIS_VERTICAL_SCROLL;
|
||||
value = -DEFAULT_AXIS_STEP_DISTANCE;
|
||||
break;
|
||||
|
||||
case CLUTTER_SCROLL_DOWN:
|
||||
axis = WL_POINTER_AXIS_VERTICAL_SCROLL;
|
||||
value = DEFAULT_AXIS_STEP_DISTANCE;
|
||||
break;
|
||||
|
||||
case CLUTTER_SCROLL_LEFT:
|
||||
axis = WL_POINTER_AXIS_HORIZONTAL_SCROLL;
|
||||
value = -DEFAULT_AXIS_STEP_DISTANCE;
|
||||
break;
|
||||
|
||||
case CLUTTER_SCROLL_RIGHT:
|
||||
axis = WL_POINTER_AXIS_HORIZONTAL_SCROLL;
|
||||
value = DEFAULT_AXIS_STEP_DISTANCE;
|
||||
break;
|
||||
|
||||
default:
|
||||
return;
|
||||
}
|
||||
|
||||
if (seat->pointer.focus_resource)
|
||||
wl_pointer_send_axis (seat->pointer.focus_resource,
|
||||
event->time,
|
||||
axis,
|
||||
value);
|
||||
}
|
||||
|
||||
void
|
||||
meta_wayland_seat_handle_event (MetaWaylandSeat *seat,
|
||||
const ClutterEvent *event)
|
||||
{
|
||||
switch (event->type)
|
||||
{
|
||||
case CLUTTER_MOTION:
|
||||
handle_motion_event (seat,
|
||||
(const ClutterMotionEvent *) event);
|
||||
break;
|
||||
|
||||
case CLUTTER_BUTTON_PRESS:
|
||||
case CLUTTER_BUTTON_RELEASE:
|
||||
handle_button_event (seat,
|
||||
(const ClutterButtonEvent *) event);
|
||||
break;
|
||||
|
||||
case CLUTTER_KEY_PRESS:
|
||||
case CLUTTER_KEY_RELEASE:
|
||||
meta_wayland_keyboard_handle_event (&seat->keyboard,
|
||||
(const ClutterKeyEvent *) event);
|
||||
break;
|
||||
|
||||
case CLUTTER_SCROLL:
|
||||
handle_scroll_event (seat, (const ClutterScrollEvent *) event);
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
update_pointer_position_for_actor (MetaWaylandPointer *pointer,
|
||||
ClutterActor *actor)
|
||||
{
|
||||
float ax, ay;
|
||||
|
||||
clutter_actor_transform_stage_point (actor,
|
||||
wl_fixed_to_double (pointer->x),
|
||||
wl_fixed_to_double (pointer->y),
|
||||
&ax, &ay);
|
||||
pointer->current_x = wl_fixed_from_double (ax);
|
||||
pointer->current_y = wl_fixed_from_double (ay);
|
||||
}
|
||||
|
||||
/* The actor argument can be NULL in which case a Clutter pick will be
|
||||
performed to determine the right actor. An actor should only be
|
||||
passed if the repick is being performed due to an event in which
|
||||
case Clutter will have already performed a pick so we can avoid
|
||||
redundantly doing another one */
|
||||
void
|
||||
meta_wayland_seat_repick (MetaWaylandSeat *seat,
|
||||
uint32_t time,
|
||||
ClutterActor *actor)
|
||||
{
|
||||
MetaWaylandPointer *pointer = &seat->pointer;
|
||||
MetaWaylandSurface *surface = NULL;
|
||||
|
||||
if (actor == NULL && seat->current_stage)
|
||||
{
|
||||
ClutterStage *stage = CLUTTER_STAGE (seat->current_stage);
|
||||
actor = clutter_stage_get_actor_at_pos (stage,
|
||||
CLUTTER_PICK_REACTIVE,
|
||||
wl_fixed_to_double (pointer->x),
|
||||
wl_fixed_to_double (pointer->y));
|
||||
}
|
||||
|
||||
if (actor)
|
||||
seat->current_stage = clutter_actor_get_stage (actor);
|
||||
else
|
||||
seat->current_stage = NULL;
|
||||
|
||||
if (META_IS_WINDOW_ACTOR (actor))
|
||||
{
|
||||
MetaWindow *window =
|
||||
meta_window_actor_get_meta_window (META_WINDOW_ACTOR (actor));
|
||||
|
||||
update_pointer_position_for_actor (pointer, actor);
|
||||
|
||||
surface = window->surface;
|
||||
}
|
||||
else if (META_IS_SHAPED_TEXTURE (actor))
|
||||
{
|
||||
MetaShapedTexture *shaped_texture = META_SHAPED_TEXTURE (actor);
|
||||
|
||||
update_pointer_position_for_actor (pointer, actor);
|
||||
|
||||
surface = meta_shaped_texture_get_wayland_surface (shaped_texture);
|
||||
}
|
||||
|
||||
pointer->current = surface;
|
||||
if (surface != pointer->focus)
|
||||
{
|
||||
const MetaWaylandPointerGrabInterface *interface =
|
||||
pointer->grab->interface;
|
||||
interface->focus (pointer->grab,
|
||||
surface,
|
||||
pointer->current_x, pointer->current_y);
|
||||
}
|
||||
|
||||
if (pointer->grab->focus)
|
||||
transform_stage_point_fixed (pointer->grab->focus,
|
||||
pointer->x,
|
||||
pointer->y,
|
||||
&pointer->grab->x,
|
||||
&pointer->grab->y);
|
||||
}
|
||||
|
||||
void
|
||||
meta_wayland_seat_free (MetaWaylandSeat *seat)
|
||||
{
|
||||
pointer_unmap_sprite (seat);
|
||||
|
||||
meta_wayland_pointer_release (&seat->pointer);
|
||||
meta_wayland_keyboard_release (&seat->keyboard);
|
||||
|
||||
wl_signal_emit (&seat->destroy_signal, seat);
|
||||
|
||||
g_slice_free (MetaWaylandSeat, seat);
|
||||
}
|
||||
108
src/wayland/meta-wayland-seat.h
Normal file
108
src/wayland/meta-wayland-seat.h
Normal file
@@ -0,0 +1,108 @@
|
||||
/*
|
||||
* Wayland Support
|
||||
*
|
||||
* Copyright (C) 2012 Intel Corporation
|
||||
*
|
||||
* 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_SEAT_H__
|
||||
#define __META_WAYLAND_SEAT_H__
|
||||
|
||||
#include <wayland-server.h>
|
||||
#include <xkbcommon/xkbcommon.h>
|
||||
#include <clutter/clutter.h>
|
||||
#include <glib.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,
|
||||
gboolean is_native);
|
||||
|
||||
void
|
||||
meta_wayland_seat_handle_event (MetaWaylandSeat *seat,
|
||||
const ClutterEvent *event);
|
||||
|
||||
void
|
||||
meta_wayland_seat_repick (MetaWaylandSeat *seat,
|
||||
uint32_t time,
|
||||
ClutterActor *actor);
|
||||
|
||||
void
|
||||
meta_wayland_seat_update_sprite (MetaWaylandSeat *seat);
|
||||
|
||||
void
|
||||
meta_wayland_seat_free (MetaWaylandSeat *seat);
|
||||
|
||||
#endif /* __META_WAYLAND_SEAT_H__ */
|
||||
68
src/wayland/meta-wayland-stage.c
Normal file
68
src/wayland/meta-wayland-stage.c
Normal file
@@ -0,0 +1,68 @@
|
||||
/*
|
||||
* Wayland Support
|
||||
*
|
||||
* Copyright (C) 2012 Intel Corporation
|
||||
*
|
||||
* 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 <clutter/clutter.h>
|
||||
#include <gdk-pixbuf/gdk-pixbuf.h>
|
||||
#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"
|
||||
|
||||
G_DEFINE_TYPE (MetaWaylandStage, meta_wayland_stage, CLUTTER_TYPE_STAGE);
|
||||
|
||||
static void
|
||||
meta_wayland_stage_paint (ClutterActor *actor)
|
||||
{
|
||||
MetaWaylandCompositor *compositor;
|
||||
|
||||
CLUTTER_ACTOR_CLASS (meta_wayland_stage_parent_class)->paint (actor);
|
||||
|
||||
compositor = meta_wayland_compositor_get_default ();
|
||||
if (compositor->seat->cursor_tracker)
|
||||
meta_cursor_tracker_paint (compositor->seat->cursor_tracker);
|
||||
}
|
||||
|
||||
static void
|
||||
meta_wayland_stage_class_init (MetaWaylandStageClass *klass)
|
||||
{
|
||||
ClutterActorClass *actor_class = (ClutterActorClass *) klass;
|
||||
|
||||
actor_class->paint = meta_wayland_stage_paint;
|
||||
}
|
||||
|
||||
static void
|
||||
meta_wayland_stage_init (MetaWaylandStage *self)
|
||||
{
|
||||
clutter_stage_set_user_resizable (CLUTTER_STAGE (self), FALSE);
|
||||
}
|
||||
|
||||
ClutterActor *
|
||||
meta_wayland_stage_new (void)
|
||||
{
|
||||
return g_object_new (META_WAYLAND_TYPE_STAGE,
|
||||
"cursor-visible", FALSE,
|
||||
NULL);
|
||||
}
|
||||
70
src/wayland/meta-wayland-stage.h
Normal file
70
src/wayland/meta-wayland-stage.h
Normal file
@@ -0,0 +1,70 @@
|
||||
/*
|
||||
* Copyright (C) 2012 Intel Corporation
|
||||
*
|
||||
* 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_STAGE_H
|
||||
#define META_WAYLAND_STAGE_H
|
||||
|
||||
#include <clutter/clutter.h>
|
||||
#include <wayland-server.h>
|
||||
|
||||
#include "window-private.h"
|
||||
|
||||
G_BEGIN_DECLS
|
||||
|
||||
#define META_WAYLAND_TYPE_STAGE \
|
||||
(meta_wayland_stage_get_type())
|
||||
#define META_WAYLAND_STAGE(obj) \
|
||||
(G_TYPE_CHECK_INSTANCE_CAST ((obj), \
|
||||
META_WAYLAND_TYPE_STAGE, \
|
||||
MetaWaylandStage))
|
||||
#define META_WAYLAND_STAGE_CLASS(klass) \
|
||||
(G_TYPE_CHECK_CLASS_CAST ((klass), \
|
||||
META_WAYLAND_TYPE_STAGE, \
|
||||
MetaWaylandStageClass))
|
||||
#define META_WAYLAND_IS_STAGE(obj) \
|
||||
(G_TYPE_CHECK_INSTANCE_TYPE ((obj), \
|
||||
META_WAYLAND_TYPE_STAGE))
|
||||
#define META_WAYLAND_IS_STAGE_CLASS(klass) \
|
||||
(G_TYPE_CHECK_CLASS_TYPE ((klass), \
|
||||
META_WAYLAND_TYPE_STAGE))
|
||||
#define META_WAYLAND_STAGE_GET_CLASS(obj) \
|
||||
(G_TYPE_INSTANCE_GET_CLASS ((obj), \
|
||||
META_WAYLAND_STAGE, \
|
||||
MetaWaylandStageClass))
|
||||
|
||||
typedef struct _MetaWaylandStage MetaWaylandStage;
|
||||
typedef struct _MetaWaylandStageClass MetaWaylandStageClass;
|
||||
|
||||
struct _MetaWaylandStageClass
|
||||
{
|
||||
ClutterStageClass parent_class;
|
||||
};
|
||||
|
||||
struct _MetaWaylandStage
|
||||
{
|
||||
ClutterStage parent;
|
||||
};
|
||||
|
||||
GType meta_wayland_stage_get_type (void) G_GNUC_CONST;
|
||||
|
||||
ClutterActor *meta_wayland_stage_new (void);
|
||||
|
||||
G_END_DECLS
|
||||
|
||||
#endif /* META_WAYLAND_STAGE_H */
|
||||
1047
src/wayland/meta-wayland-surface.c
Normal file
1047
src/wayland/meta-wayland-surface.c
Normal file
File diff suppressed because it is too large
Load Diff
119
src/wayland/meta-wayland-surface.h
Normal file
119
src/wayland/meta-wayland-surface.h
Normal 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
|
||||
40
src/wayland/meta-wayland-types.h
Normal file
40
src/wayland/meta-wayland-types.h
Normal 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
|
||||
947
src/wayland/meta-wayland.c
Normal file
947
src/wayland/meta-wayland.c
Normal file
@@ -0,0 +1,947 @@
|
||||
/*
|
||||
* Wayland Support
|
||||
*
|
||||
* Copyright (C) 2012,2013 Intel Corporation
|
||||
*
|
||||
* 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 <clutter/clutter.h>
|
||||
#include <clutter/wayland/clutter-wayland-compositor.h>
|
||||
#include <clutter/wayland/clutter-wayland-surface.h>
|
||||
|
||||
#include <glib.h>
|
||||
#include <sys/time.h>
|
||||
#include <string.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/stat.h>
|
||||
#include <stdlib.h>
|
||||
#include <sys/wait.h>
|
||||
|
||||
#include <wayland-server.h>
|
||||
|
||||
#include "meta-wayland-private.h"
|
||||
#include "meta-xwayland-private.h"
|
||||
#include "meta-wayland-stage.h"
|
||||
#include "meta-window-actor-private.h"
|
||||
#include "meta-wayland-seat.h"
|
||||
#include "meta-wayland-keyboard.h"
|
||||
#include "meta-wayland-pointer.h"
|
||||
#include "meta-wayland-data-device.h"
|
||||
#include "meta-cursor-tracker-private.h"
|
||||
#include "display-private.h"
|
||||
#include "window-private.h"
|
||||
#include <meta/types.h>
|
||||
#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;
|
||||
|
||||
MetaWaylandCompositor *
|
||||
meta_wayland_compositor_get_default (void)
|
||||
{
|
||||
return &_meta_wayland_compositor;
|
||||
}
|
||||
|
||||
static guint32
|
||||
get_time (void)
|
||||
{
|
||||
struct timeval tv;
|
||||
gettimeofday (&tv, NULL);
|
||||
return tv.tv_sec * 1000 + tv.tv_usec / 1000;
|
||||
}
|
||||
|
||||
static gboolean
|
||||
wayland_event_source_prepare (GSource *base, int *timeout)
|
||||
{
|
||||
WaylandEventSource *source = (WaylandEventSource *)base;
|
||||
|
||||
*timeout = -1;
|
||||
|
||||
wl_display_flush_clients (source->display);
|
||||
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
static gboolean
|
||||
wayland_event_source_check (GSource *base)
|
||||
{
|
||||
WaylandEventSource *source = (WaylandEventSource *)base;
|
||||
return source->pfd.revents;
|
||||
}
|
||||
|
||||
static gboolean
|
||||
wayland_event_source_dispatch (GSource *base,
|
||||
GSourceFunc callback,
|
||||
void *data)
|
||||
{
|
||||
WaylandEventSource *source = (WaylandEventSource *)base;
|
||||
struct wl_event_loop *loop = wl_display_get_event_loop (source->display);
|
||||
|
||||
wl_event_loop_dispatch (loop, 0);
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static GSourceFuncs wayland_event_source_funcs =
|
||||
{
|
||||
wayland_event_source_prepare,
|
||||
wayland_event_source_check,
|
||||
wayland_event_source_dispatch,
|
||||
NULL
|
||||
};
|
||||
|
||||
static GSource *
|
||||
wayland_event_source_new (struct wl_display *display)
|
||||
{
|
||||
WaylandEventSource *source;
|
||||
struct wl_event_loop *loop = wl_display_get_event_loop (display);
|
||||
|
||||
source = (WaylandEventSource *) g_source_new (&wayland_event_source_funcs,
|
||||
sizeof (WaylandEventSource));
|
||||
source->display = display;
|
||||
source->pfd.fd = wl_event_loop_get_fd (loop);
|
||||
source->pfd.events = G_IO_IN | G_IO_ERR;
|
||||
g_source_add_poll (&source->source, &source->pfd);
|
||||
|
||||
return &source->source;
|
||||
}
|
||||
|
||||
static void
|
||||
meta_wayland_buffer_destroy_handler (struct wl_listener *listener,
|
||||
void *data)
|
||||
{
|
||||
MetaWaylandBuffer *buffer =
|
||||
wl_container_of (listener, buffer, destroy_listener);
|
||||
|
||||
wl_signal_emit (&buffer->destroy_signal, buffer);
|
||||
g_slice_free (MetaWaylandBuffer, buffer);
|
||||
}
|
||||
|
||||
MetaWaylandBuffer *
|
||||
meta_wayland_buffer_from_resource (struct wl_resource *resource)
|
||||
{
|
||||
MetaWaylandBuffer *buffer;
|
||||
struct wl_listener *listener;
|
||||
|
||||
listener =
|
||||
wl_resource_get_destroy_listener (resource,
|
||||
meta_wayland_buffer_destroy_handler);
|
||||
|
||||
if (listener)
|
||||
{
|
||||
buffer = wl_container_of (listener, buffer, destroy_listener);
|
||||
}
|
||||
else
|
||||
{
|
||||
buffer = g_slice_new0 (MetaWaylandBuffer);
|
||||
|
||||
buffer->resource = resource;
|
||||
wl_signal_init (&buffer->destroy_signal);
|
||||
buffer->destroy_listener.notify = meta_wayland_buffer_destroy_handler;
|
||||
wl_resource_add_destroy_listener (resource, &buffer->destroy_listener);
|
||||
}
|
||||
|
||||
return buffer;
|
||||
}
|
||||
|
||||
static void
|
||||
meta_wayland_buffer_reference_handle_destroy (struct wl_listener *listener,
|
||||
void *data)
|
||||
{
|
||||
MetaWaylandBufferReference *ref =
|
||||
wl_container_of (listener, ref, destroy_listener);
|
||||
|
||||
g_assert (data == ref->buffer);
|
||||
|
||||
ref->buffer = NULL;
|
||||
}
|
||||
|
||||
void
|
||||
meta_wayland_buffer_reference (MetaWaylandBufferReference *ref,
|
||||
MetaWaylandBuffer *buffer)
|
||||
{
|
||||
if (ref->buffer && buffer != ref->buffer)
|
||||
{
|
||||
ref->buffer->busy_count--;
|
||||
|
||||
if (ref->buffer->busy_count == 0)
|
||||
{
|
||||
g_assert (wl_resource_get_client (ref->buffer->resource));
|
||||
wl_resource_queue_event (ref->buffer->resource, WL_BUFFER_RELEASE);
|
||||
}
|
||||
|
||||
wl_list_remove (&ref->destroy_listener.link);
|
||||
}
|
||||
|
||||
if (buffer && buffer != ref->buffer)
|
||||
{
|
||||
buffer->busy_count++;
|
||||
wl_signal_add (&buffer->destroy_signal, &ref->destroy_listener);
|
||||
}
|
||||
|
||||
ref->buffer = buffer;
|
||||
ref->destroy_listener.notify = meta_wayland_buffer_reference_handle_destroy;
|
||||
}
|
||||
|
||||
void
|
||||
meta_wayland_compositor_set_input_focus (MetaWaylandCompositor *compositor,
|
||||
MetaWindow *window)
|
||||
{
|
||||
MetaWaylandSurface *surface = window ? window->surface : NULL;
|
||||
|
||||
meta_wayland_keyboard_set_focus (&compositor->seat->keyboard,
|
||||
surface);
|
||||
meta_wayland_data_device_set_keyboard_focus (compositor->seat);
|
||||
}
|
||||
|
||||
void
|
||||
meta_wayland_compositor_repick (MetaWaylandCompositor *compositor)
|
||||
{
|
||||
meta_wayland_seat_repick (compositor->seat,
|
||||
get_time (),
|
||||
NULL);
|
||||
}
|
||||
|
||||
static void
|
||||
meta_wayland_compositor_create_surface (struct wl_client *wayland_client,
|
||||
struct wl_resource *wayland_compositor_resource,
|
||||
guint32 id)
|
||||
{
|
||||
MetaWaylandCompositor *compositor =
|
||||
wl_resource_get_user_data (wayland_compositor_resource);
|
||||
MetaWaylandSurface *surface;
|
||||
|
||||
/* a surface inherits the version from the compositor */
|
||||
surface = meta_wayland_surface_create (compositor,
|
||||
wayland_client,
|
||||
id,
|
||||
wl_resource_get_version (wayland_compositor_resource));
|
||||
|
||||
compositor->surfaces = g_list_prepend (compositor->surfaces, surface);
|
||||
}
|
||||
|
||||
static void
|
||||
meta_wayland_region_destroy (struct wl_client *client,
|
||||
struct wl_resource *resource)
|
||||
{
|
||||
wl_resource_destroy (resource);
|
||||
}
|
||||
|
||||
static void
|
||||
meta_wayland_region_add (struct wl_client *client,
|
||||
struct wl_resource *resource,
|
||||
gint32 x,
|
||||
gint32 y,
|
||||
gint32 width,
|
||||
gint32 height)
|
||||
{
|
||||
MetaWaylandRegion *region = wl_resource_get_user_data (resource);
|
||||
cairo_rectangle_int_t rectangle = { x, y, width, height };
|
||||
|
||||
cairo_region_union_rectangle (region->region, &rectangle);
|
||||
}
|
||||
|
||||
static void
|
||||
meta_wayland_region_subtract (struct wl_client *client,
|
||||
struct wl_resource *resource,
|
||||
gint32 x,
|
||||
gint32 y,
|
||||
gint32 width,
|
||||
gint32 height)
|
||||
{
|
||||
MetaWaylandRegion *region = wl_resource_get_user_data (resource);
|
||||
cairo_rectangle_int_t rectangle = { x, y, width, height };
|
||||
|
||||
cairo_region_subtract_rectangle (region->region, &rectangle);
|
||||
}
|
||||
|
||||
const struct wl_region_interface meta_wayland_region_interface = {
|
||||
meta_wayland_region_destroy,
|
||||
meta_wayland_region_add,
|
||||
meta_wayland_region_subtract
|
||||
};
|
||||
|
||||
static void
|
||||
meta_wayland_region_resource_destroy_cb (struct wl_resource *resource)
|
||||
{
|
||||
MetaWaylandRegion *region = wl_resource_get_user_data (resource);
|
||||
|
||||
cairo_region_destroy (region->region);
|
||||
g_slice_free (MetaWaylandRegion, region);
|
||||
}
|
||||
|
||||
static void
|
||||
meta_wayland_compositor_create_region (struct wl_client *wayland_client,
|
||||
struct wl_resource *compositor_resource,
|
||||
uint32_t id)
|
||||
{
|
||||
MetaWaylandRegion *region = g_slice_new0 (MetaWaylandRegion);
|
||||
|
||||
region->resource = wl_resource_create (wayland_client,
|
||||
&wl_region_interface, 1,
|
||||
id);
|
||||
wl_resource_set_implementation (region->resource,
|
||||
&meta_wayland_region_interface, region,
|
||||
meta_wayland_region_resource_destroy_cb);
|
||||
|
||||
region->region = cairo_region_create ();
|
||||
}
|
||||
|
||||
typedef struct {
|
||||
MetaOutput *output;
|
||||
struct wl_global *global;
|
||||
int x, y;
|
||||
enum wl_output_transform transform;
|
||||
|
||||
GList *resources;
|
||||
} MetaWaylandOutput;
|
||||
|
||||
static void
|
||||
output_resource_destroy (struct wl_resource *res)
|
||||
{
|
||||
MetaWaylandOutput *wayland_output;
|
||||
|
||||
wayland_output = wl_resource_get_user_data (res);
|
||||
wayland_output->resources = g_list_remove (wayland_output->resources, res);
|
||||
}
|
||||
|
||||
static void
|
||||
bind_output (struct wl_client *client,
|
||||
void *data,
|
||||
guint32 version,
|
||||
guint32 id)
|
||||
{
|
||||
MetaWaylandOutput *wayland_output = data;
|
||||
MetaOutput *output = wayland_output->output;
|
||||
struct wl_resource *resource;
|
||||
guint mode_flags;
|
||||
|
||||
resource = wl_resource_create (client, &wl_output_interface, version, id);
|
||||
wayland_output->resources = g_list_prepend (wayland_output->resources, resource);
|
||||
|
||||
wl_resource_set_user_data (resource, wayland_output);
|
||||
wl_resource_set_destructor (resource, output_resource_destroy);
|
||||
|
||||
meta_verbose ("Binding output %p/%s (%u, %u, %u, %u) x %f\n",
|
||||
output, output->name,
|
||||
output->crtc->rect.x, output->crtc->rect.y,
|
||||
output->crtc->rect.width, output->crtc->rect.height,
|
||||
output->crtc->current_mode->refresh_rate);
|
||||
|
||||
wl_resource_post_event (resource,
|
||||
WL_OUTPUT_GEOMETRY,
|
||||
(int)output->crtc->rect.x,
|
||||
(int)output->crtc->rect.y,
|
||||
output->width_mm,
|
||||
output->height_mm,
|
||||
/* Cogl values reflect XRandR values,
|
||||
and so does wayland */
|
||||
output->subpixel_order,
|
||||
output->vendor,
|
||||
output->product,
|
||||
output->crtc->transform);
|
||||
|
||||
g_assert (output->crtc->current_mode != NULL);
|
||||
|
||||
mode_flags = WL_OUTPUT_MODE_CURRENT;
|
||||
if (output->crtc->current_mode == output->preferred_mode)
|
||||
mode_flags |= WL_OUTPUT_MODE_PREFERRED;
|
||||
|
||||
wl_resource_post_event (resource,
|
||||
WL_OUTPUT_MODE,
|
||||
mode_flags,
|
||||
(int)output->crtc->current_mode->width,
|
||||
(int)output->crtc->current_mode->height,
|
||||
(int)output->crtc->current_mode->refresh_rate);
|
||||
|
||||
if (version >= 2)
|
||||
wl_resource_post_event (resource,
|
||||
WL_OUTPUT_DONE);
|
||||
}
|
||||
|
||||
static void
|
||||
wayland_output_destroy_notify (gpointer data)
|
||||
{
|
||||
MetaWaylandOutput *wayland_output = data;
|
||||
GList *resources;
|
||||
|
||||
/* Make sure the destructors don't mess with the list */
|
||||
resources = wayland_output->resources;
|
||||
wayland_output->resources = NULL;
|
||||
|
||||
wl_global_destroy (wayland_output->global);
|
||||
g_list_free (resources);
|
||||
|
||||
g_slice_free (MetaWaylandOutput, wayland_output);
|
||||
}
|
||||
|
||||
static void
|
||||
wayland_output_update_for_output (MetaWaylandOutput *wayland_output,
|
||||
MetaOutput *output)
|
||||
{
|
||||
GList *iter;
|
||||
guint mode_flags;
|
||||
|
||||
g_assert (output->crtc->current_mode != NULL);
|
||||
|
||||
mode_flags = WL_OUTPUT_MODE_CURRENT;
|
||||
if (output->crtc->current_mode == output->preferred_mode)
|
||||
mode_flags |= WL_OUTPUT_MODE_PREFERRED;
|
||||
|
||||
for (iter = wayland_output->resources; iter; iter = iter->next)
|
||||
{
|
||||
struct wl_resource *resource = iter->data;
|
||||
|
||||
if (wayland_output->x != output->crtc->rect.x ||
|
||||
wayland_output->y != output->crtc->rect.y ||
|
||||
wayland_output->transform != output->crtc->transform)
|
||||
{
|
||||
wl_resource_post_event (resource,
|
||||
WL_OUTPUT_GEOMETRY,
|
||||
(int)output->crtc->rect.x,
|
||||
(int)output->crtc->rect.y,
|
||||
output->width_mm,
|
||||
output->height_mm,
|
||||
output->subpixel_order,
|
||||
output->vendor,
|
||||
output->product,
|
||||
output->crtc->transform);
|
||||
}
|
||||
|
||||
wl_resource_post_event (resource,
|
||||
WL_OUTPUT_MODE,
|
||||
mode_flags,
|
||||
(int)output->crtc->current_mode->width,
|
||||
(int)output->crtc->current_mode->height,
|
||||
(int)output->crtc->current_mode->refresh_rate);
|
||||
}
|
||||
|
||||
/* It's very important that we change the output pointer here, as
|
||||
the old structure is about to be freed by MetaMonitorManager */
|
||||
wayland_output->output = output;
|
||||
wayland_output->x = output->crtc->rect.x;
|
||||
wayland_output->y = output->crtc->rect.y;
|
||||
wayland_output->transform = output->crtc->transform;
|
||||
}
|
||||
|
||||
static GHashTable *
|
||||
meta_wayland_compositor_update_outputs (MetaWaylandCompositor *compositor,
|
||||
MetaMonitorManager *monitors)
|
||||
{
|
||||
MetaOutput *outputs;
|
||||
unsigned int i, n_outputs;
|
||||
GHashTable *new_table;
|
||||
|
||||
outputs = meta_monitor_manager_get_outputs (monitors, &n_outputs);
|
||||
new_table = g_hash_table_new_full (NULL, NULL, NULL, wayland_output_destroy_notify);
|
||||
|
||||
for (i = 0; i < n_outputs; i++)
|
||||
{
|
||||
MetaOutput *output = &outputs[i];
|
||||
MetaWaylandOutput *wayland_output;
|
||||
|
||||
/* wayland does not expose disabled outputs */
|
||||
if (output->crtc == NULL)
|
||||
{
|
||||
g_hash_table_remove (compositor->outputs, GSIZE_TO_POINTER (output->output_id));
|
||||
continue;
|
||||
}
|
||||
|
||||
wayland_output = g_hash_table_lookup (compositor->outputs, GSIZE_TO_POINTER (output->output_id));
|
||||
|
||||
if (wayland_output)
|
||||
{
|
||||
g_hash_table_steal (compositor->outputs, GSIZE_TO_POINTER (output->output_id));
|
||||
}
|
||||
else
|
||||
{
|
||||
wayland_output = g_slice_new0 (MetaWaylandOutput);
|
||||
wayland_output->global = wl_global_create (compositor->wayland_display,
|
||||
&wl_output_interface, 2,
|
||||
wayland_output, bind_output);
|
||||
}
|
||||
|
||||
wayland_output_update_for_output (wayland_output, output);
|
||||
g_hash_table_insert (new_table, GSIZE_TO_POINTER (output->output_id), wayland_output);
|
||||
}
|
||||
|
||||
g_hash_table_destroy (compositor->outputs);
|
||||
return new_table;
|
||||
}
|
||||
|
||||
const static struct wl_compositor_interface meta_wayland_compositor_interface = {
|
||||
meta_wayland_compositor_create_surface,
|
||||
meta_wayland_compositor_create_region
|
||||
};
|
||||
|
||||
static void
|
||||
paint_finished_cb (ClutterActor *self, void *user_data)
|
||||
{
|
||||
MetaWaylandCompositor *compositor = user_data;
|
||||
|
||||
while (!wl_list_empty (&compositor->frame_callbacks))
|
||||
{
|
||||
MetaWaylandFrameCallback *callback =
|
||||
wl_container_of (compositor->frame_callbacks.next, callback, link);
|
||||
|
||||
wl_resource_post_event (callback->resource,
|
||||
WL_CALLBACK_DONE, get_time ());
|
||||
wl_resource_destroy (callback->resource);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
compositor_bind (struct wl_client *client,
|
||||
void *data,
|
||||
guint32 version,
|
||||
guint32 id)
|
||||
{
|
||||
MetaWaylandCompositor *compositor = data;
|
||||
struct wl_resource *resource;
|
||||
|
||||
resource = wl_resource_create (client, &wl_compositor_interface, version, id);
|
||||
wl_resource_set_implementation (resource, &meta_wayland_compositor_interface, compositor, NULL);
|
||||
}
|
||||
|
||||
static void
|
||||
stage_destroy_cb (void)
|
||||
{
|
||||
meta_quit (META_EXIT_SUCCESS);
|
||||
}
|
||||
|
||||
#define N_BUTTONS 5
|
||||
|
||||
static void
|
||||
synthesize_motion_event (MetaWaylandCompositor *compositor,
|
||||
const ClutterEvent *event)
|
||||
{
|
||||
/* We want to synthesize X events for mouse motion events so that we
|
||||
don't have to rely on the X server's window position being
|
||||
synched with the surface position. See the comment in
|
||||
event_callback() in display.c */
|
||||
MetaWaylandSeat *seat = compositor->seat;
|
||||
MetaWaylandPointer *pointer = &seat->pointer;
|
||||
MetaWaylandSurface *surface;
|
||||
XGenericEventCookie generic_event;
|
||||
XIDeviceEvent device_event;
|
||||
unsigned char button_mask[(N_BUTTONS + 7) / 8] = { 0 };
|
||||
MetaDisplay *display = meta_get_display ();
|
||||
ClutterModifierType state;
|
||||
int i;
|
||||
|
||||
generic_event.type = GenericEvent;
|
||||
generic_event.serial = 0;
|
||||
generic_event.send_event = False;
|
||||
generic_event.display = display->xdisplay;
|
||||
generic_event.extension = display->xinput_opcode;
|
||||
generic_event.evtype = XI_Motion;
|
||||
/* Mutter assumes the data for the event is already retrieved by GDK
|
||||
* so we don't need the cookie */
|
||||
generic_event.cookie = 0;
|
||||
generic_event.data = &device_event;
|
||||
|
||||
memcpy (&device_event, &generic_event, sizeof (XGenericEvent));
|
||||
|
||||
device_event.time = clutter_event_get_time (event);
|
||||
device_event.deviceid = clutter_event_get_device_id (event);
|
||||
device_event.sourceid = 0; /* not used, not sure what this should be */
|
||||
device_event.detail = 0;
|
||||
device_event.root = DefaultRootWindow (display->xdisplay);
|
||||
device_event.flags = 0 /* not used for motion events */;
|
||||
|
||||
if (compositor->implicit_grab_surface)
|
||||
surface = compositor->implicit_grab_surface;
|
||||
else
|
||||
surface = pointer->current;
|
||||
|
||||
if (surface == pointer->current)
|
||||
{
|
||||
device_event.event_x = wl_fixed_to_int (pointer->current_x);
|
||||
device_event.event_y = wl_fixed_to_int (pointer->current_y);
|
||||
}
|
||||
else if (surface && surface->window)
|
||||
{
|
||||
ClutterActor *window_actor =
|
||||
CLUTTER_ACTOR (meta_window_get_compositor_private (surface->window));
|
||||
|
||||
if (window_actor)
|
||||
{
|
||||
float ax, ay;
|
||||
|
||||
clutter_actor_transform_stage_point (window_actor,
|
||||
wl_fixed_to_double (pointer->x),
|
||||
wl_fixed_to_double (pointer->y),
|
||||
&ax, &ay);
|
||||
|
||||
device_event.event_x = ax;
|
||||
device_event.event_y = ay;
|
||||
}
|
||||
else
|
||||
{
|
||||
device_event.event_x = wl_fixed_to_double (pointer->x);
|
||||
device_event.event_y = wl_fixed_to_double (pointer->y);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
device_event.event_x = wl_fixed_to_double (pointer->x);
|
||||
device_event.event_y = wl_fixed_to_double (pointer->y);
|
||||
}
|
||||
|
||||
if (surface && surface->xid != None)
|
||||
device_event.event = surface->xid;
|
||||
else
|
||||
device_event.event = device_event.root;
|
||||
|
||||
/* Mutter doesn't really know about the sub-windows. This assumes it
|
||||
doesn't care either */
|
||||
device_event.child = device_event.event;
|
||||
device_event.root_x = wl_fixed_to_double (pointer->x);
|
||||
device_event.root_y = wl_fixed_to_double (pointer->y);
|
||||
|
||||
state = clutter_event_get_state (event);
|
||||
|
||||
for (i = 0; i < N_BUTTONS; i++)
|
||||
if ((state & (CLUTTER_BUTTON1_MASK << i)))
|
||||
XISetMask (button_mask, i + 1);
|
||||
device_event.buttons.mask_len = N_BUTTONS + 1;
|
||||
device_event.buttons.mask = button_mask;
|
||||
|
||||
device_event.valuators.mask_len = 0;
|
||||
device_event.valuators.mask = NULL;
|
||||
device_event.valuators.values = NULL;
|
||||
|
||||
memset (&device_event.mods, 0, sizeof (device_event.mods));
|
||||
device_event.mods.effective =
|
||||
state & (CLUTTER_MODIFIER_MASK &
|
||||
~(((CLUTTER_BUTTON1_MASK << N_BUTTONS) - 1) ^
|
||||
(CLUTTER_BUTTON1_MASK - 1)));
|
||||
|
||||
memset (&device_event.group, 0, sizeof (device_event.group));
|
||||
|
||||
meta_display_handle_event (display, (XEvent *) &generic_event);
|
||||
}
|
||||
|
||||
static void
|
||||
reset_idletimes (const ClutterEvent *event)
|
||||
{
|
||||
ClutterInputDevice *device, *source_device;
|
||||
MetaIdleMonitor *core_monitor, *device_monitor;
|
||||
int device_id;
|
||||
|
||||
device = clutter_event_get_device (event);
|
||||
device_id = clutter_input_device_get_device_id (device);
|
||||
|
||||
core_monitor = meta_idle_monitor_get_core ();
|
||||
device_monitor = meta_idle_monitor_get_for_device (device_id);
|
||||
|
||||
meta_idle_monitor_reset_idletime (core_monitor);
|
||||
meta_idle_monitor_reset_idletime (device_monitor);
|
||||
|
||||
source_device = clutter_event_get_source_device (event);
|
||||
if (source_device != device)
|
||||
{
|
||||
device_id = clutter_input_device_get_device_id (device);
|
||||
device_monitor = meta_idle_monitor_get_for_device (device_id);
|
||||
meta_idle_monitor_reset_idletime (device_monitor);
|
||||
}
|
||||
}
|
||||
|
||||
static gboolean
|
||||
event_cb (ClutterActor *stage,
|
||||
const ClutterEvent *event,
|
||||
MetaWaylandCompositor *compositor)
|
||||
{
|
||||
MetaWaylandSeat *seat = compositor->seat;
|
||||
MetaWaylandPointer *pointer = &seat->pointer;
|
||||
MetaWaylandSurface *surface;
|
||||
MetaDisplay *display;
|
||||
|
||||
reset_idletimes (event);
|
||||
|
||||
meta_wayland_seat_handle_event (compositor->seat, event);
|
||||
|
||||
/* HACK: for now, the surfaces from Wayland clients aren't
|
||||
integrated into Mutter's event handling and Mutter won't give them
|
||||
focus on mouse clicks. As a hack to work around this we can just
|
||||
give them input focus on mouse clicks so we can at least test the
|
||||
keyboard support */
|
||||
if (event->type == CLUTTER_BUTTON_PRESS)
|
||||
{
|
||||
surface = pointer->current;
|
||||
|
||||
if (surface && 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_focus (surface->window, timestamp);
|
||||
}
|
||||
}
|
||||
|
||||
if (seat->cursor_tracker)
|
||||
{
|
||||
meta_cursor_tracker_update_position (seat->cursor_tracker,
|
||||
wl_fixed_to_int (pointer->x),
|
||||
wl_fixed_to_int (pointer->y));
|
||||
|
||||
if (pointer->current == NULL)
|
||||
meta_cursor_tracker_revert_root (seat->cursor_tracker);
|
||||
|
||||
meta_cursor_tracker_queue_redraw (seat->cursor_tracker, stage);
|
||||
}
|
||||
|
||||
display = meta_get_display ();
|
||||
if (!display)
|
||||
return FALSE;
|
||||
|
||||
switch (event->type)
|
||||
{
|
||||
case CLUTTER_BUTTON_PRESS:
|
||||
if (compositor->implicit_grab_surface == NULL)
|
||||
{
|
||||
compositor->implicit_grab_button = event->button.button;
|
||||
compositor->implicit_grab_surface = pointer->current;
|
||||
}
|
||||
return FALSE;
|
||||
|
||||
case CLUTTER_BUTTON_RELEASE:
|
||||
if (event->type == CLUTTER_BUTTON_RELEASE &&
|
||||
compositor->implicit_grab_surface &&
|
||||
event->button.button == compositor->implicit_grab_button)
|
||||
compositor->implicit_grab_surface = NULL;
|
||||
return FALSE;
|
||||
|
||||
case CLUTTER_MOTION:
|
||||
synthesize_motion_event (compositor, event);
|
||||
return FALSE;
|
||||
|
||||
default:
|
||||
return FALSE;
|
||||
}
|
||||
}
|
||||
|
||||
static gboolean
|
||||
event_emission_hook_cb (GSignalInvocationHint *ihint,
|
||||
guint n_param_values,
|
||||
const GValue *param_values,
|
||||
gpointer data)
|
||||
{
|
||||
MetaWaylandCompositor *compositor = data;
|
||||
ClutterActor *actor;
|
||||
ClutterEvent *event;
|
||||
|
||||
g_return_val_if_fail (n_param_values == 2, FALSE);
|
||||
|
||||
actor = g_value_get_object (param_values + 0);
|
||||
event = g_value_get_boxed (param_values + 1);
|
||||
|
||||
if (actor == NULL)
|
||||
return TRUE /* stay connected */;
|
||||
|
||||
/* If this event belongs to the corresponding grab for this event
|
||||
* type then the captured-event signal won't be emitted so we have
|
||||
* to manually forward it on */
|
||||
|
||||
switch (event->type)
|
||||
{
|
||||
/* Pointer events */
|
||||
case CLUTTER_MOTION:
|
||||
case CLUTTER_ENTER:
|
||||
case CLUTTER_LEAVE:
|
||||
case CLUTTER_BUTTON_PRESS:
|
||||
case CLUTTER_BUTTON_RELEASE:
|
||||
case CLUTTER_SCROLL:
|
||||
if (actor == clutter_get_pointer_grab ())
|
||||
event_cb (clutter_actor_get_stage (actor),
|
||||
event,
|
||||
compositor);
|
||||
break;
|
||||
|
||||
/* Keyboard events */
|
||||
case CLUTTER_KEY_PRESS:
|
||||
case CLUTTER_KEY_RELEASE:
|
||||
if (actor == clutter_get_keyboard_grab ())
|
||||
event_cb (clutter_actor_get_stage (actor),
|
||||
event,
|
||||
compositor);
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return TRUE /* stay connected */;
|
||||
}
|
||||
|
||||
static void
|
||||
on_monitors_changed (MetaMonitorManager *monitors,
|
||||
MetaWaylandCompositor *compositor)
|
||||
{
|
||||
compositor->outputs = meta_wayland_compositor_update_outputs (compositor, monitors);
|
||||
}
|
||||
|
||||
void
|
||||
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));
|
||||
|
||||
compositor->wayland_display = wl_display_create ();
|
||||
if (compositor->wayland_display == NULL)
|
||||
g_error ("failed to create wayland display");
|
||||
|
||||
wl_display_init_shm (compositor->wayland_display);
|
||||
|
||||
wl_list_init (&compositor->frame_callbacks);
|
||||
|
||||
if (!wl_global_create (compositor->wayland_display,
|
||||
&wl_compositor_interface, 3,
|
||||
compositor, compositor_bind))
|
||||
g_error ("Failed to register wayland compositor object");
|
||||
|
||||
compositor->wayland_loop =
|
||||
wl_display_get_event_loop (compositor->wayland_display);
|
||||
compositor->wayland_event_source =
|
||||
wayland_event_source_new (compositor->wayland_display);
|
||||
|
||||
/* XXX: Here we are setting the wayland event source to have a
|
||||
* slightly lower priority than the X event source, because we are
|
||||
* much more likely to get confused being told about surface changes
|
||||
* relating to X clients when we don't know what's happened to them
|
||||
* according to the X protocol.
|
||||
*
|
||||
* At some point we could perhaps try and get the X protocol proxied
|
||||
* over the wayland protocol so that we don't have to worry about
|
||||
* synchronizing the two command streams. */
|
||||
g_source_set_priority (compositor->wayland_event_source,
|
||||
GDK_PRIORITY_EVENTS + 1);
|
||||
g_source_attach (compositor->wayland_event_source, NULL);
|
||||
|
||||
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",
|
||||
G_CALLBACK (on_monitors_changed), compositor);
|
||||
|
||||
compositor->outputs = g_hash_table_new_full (NULL, NULL, NULL, wayland_output_destroy_notify);
|
||||
compositor->outputs = meta_wayland_compositor_update_outputs (compositor, monitors);
|
||||
|
||||
compositor->stage = meta_wayland_stage_new ();
|
||||
g_signal_connect_after (compositor->stage, "paint",
|
||||
G_CALLBACK (paint_finished_cb), compositor);
|
||||
g_signal_connect (compositor->stage, "destroy",
|
||||
G_CALLBACK (stage_destroy_cb), NULL);
|
||||
|
||||
meta_wayland_data_device_manager_init (compositor->wayland_display);
|
||||
|
||||
compositor->seat = meta_wayland_seat_new (compositor->wayland_display,
|
||||
compositor->drm_fd >= 0);
|
||||
|
||||
g_signal_connect (compositor->stage,
|
||||
"captured-event",
|
||||
G_CALLBACK (event_cb),
|
||||
compositor);
|
||||
/* If something sets a grab on an actor then the captured event
|
||||
* signal won't get emitted but we still want to see these events so
|
||||
* we can update the cursor position. To make sure we see all events
|
||||
* we also install an emission hook on the event signal */
|
||||
event_signal = g_signal_lookup ("event", CLUTTER_TYPE_STAGE);
|
||||
g_signal_add_emission_hook (event_signal,
|
||||
0 /* detail */,
|
||||
event_emission_hook_cb,
|
||||
compositor, /* hook_data */
|
||||
NULL /* data_destroy */);
|
||||
|
||||
meta_wayland_init_shell (compositor);
|
||||
|
||||
clutter_actor_show (compositor->stage);
|
||||
|
||||
if (wl_display_add_socket (compositor->wayland_display, "wayland-0"))
|
||||
g_error ("Failed to create socket");
|
||||
|
||||
/* XXX: It's important that we only try and start xwayland after we
|
||||
* have initialized EGL because EGL implements the "wl_drm"
|
||||
* interface which xwayland requires to determine what drm device
|
||||
* name it should use.
|
||||
*
|
||||
* By waiting until we've shown the stage above we ensure that the
|
||||
* underlying GL resources for the surface have also been allocated
|
||||
* and so EGL must be initialized by this point.
|
||||
*/
|
||||
|
||||
if (!meta_xwayland_start (compositor))
|
||||
g_error ("Failed to start X Wayland");
|
||||
|
||||
putenv (g_strdup_printf ("DISPLAY=:%d", compositor->xwayland_display_index));
|
||||
}
|
||||
|
||||
void
|
||||
meta_wayland_finalize (void)
|
||||
{
|
||||
MetaWaylandCompositor *compositor;
|
||||
|
||||
compositor = meta_wayland_compositor_get_default ();
|
||||
|
||||
meta_xwayland_stop (compositor);
|
||||
g_clear_object (&compositor->launcher);
|
||||
}
|
||||
|
||||
MetaLauncher *
|
||||
meta_wayland_compositor_get_launcher (MetaWaylandCompositor *compositor)
|
||||
{
|
||||
return compositor->launcher;
|
||||
}
|
||||
453
src/wayland/meta-weston-launch.c
Normal file
453
src/wayland/meta-weston-launch.c
Normal 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);
|
||||
}
|
||||
|
||||
55
src/wayland/meta-weston-launch.h
Normal file
55
src/wayland/meta-weston-launch.h
Normal 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
|
||||
36
src/wayland/meta-xwayland-private.h
Normal file
36
src/wayland/meta-xwayland-private.h
Normal file
@@ -0,0 +1,36 @@
|
||||
/*
|
||||
* Copyright (C) 2013 Intel Corporation
|
||||
*
|
||||
* 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_XWAYLAND_PRIVATE_H
|
||||
#define META_XWAYLAND_PRIVATE_H
|
||||
|
||||
#include "meta-wayland-private.h"
|
||||
|
||||
#include <glib.h>
|
||||
|
||||
gboolean
|
||||
meta_xwayland_start (MetaWaylandCompositor *compositor);
|
||||
|
||||
void
|
||||
meta_xwayland_complete_init (void);
|
||||
|
||||
void
|
||||
meta_xwayland_stop (MetaWaylandCompositor *compositor);
|
||||
|
||||
#endif /* META_XWAYLAND_PRIVATE_H */
|
||||
461
src/wayland/meta-xwayland.c
Normal file
461
src/wayland/meta-xwayland.c
Normal file
@@ -0,0 +1,461 @@
|
||||
/*
|
||||
* X Wayland Support
|
||||
*
|
||||
* Copyright (C) 2013 Intel Corporation
|
||||
*
|
||||
* 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 <glib.h>
|
||||
|
||||
#include <unistd.h>
|
||||
#include <fcntl.h>
|
||||
#include <errno.h>
|
||||
#include <sys/socket.h>
|
||||
#include <sys/un.h>
|
||||
#include <sys/wait.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#include "meta-xwayland-private.h"
|
||||
#include "meta-window-actor-private.h"
|
||||
#include "xserver-server-protocol.h"
|
||||
|
||||
static void
|
||||
xserver_set_window_id (struct wl_client *client,
|
||||
struct wl_resource *compositor_resource,
|
||||
struct wl_resource *surface_resource,
|
||||
guint32 xid)
|
||||
{
|
||||
MetaWaylandCompositor *compositor =
|
||||
wl_resource_get_user_data (compositor_resource);
|
||||
MetaWaylandSurface *surface = wl_resource_get_user_data (surface_resource);
|
||||
MetaDisplay *display = meta_get_display ();
|
||||
MetaWindow *window;
|
||||
|
||||
g_return_if_fail (surface->xid == None);
|
||||
|
||||
surface->xid = xid;
|
||||
|
||||
window = meta_display_lookup_x_window (display, xid);
|
||||
if (window)
|
||||
{
|
||||
MetaWindowActor *window_actor =
|
||||
META_WINDOW_ACTOR (meta_window_get_compositor_private (window));
|
||||
|
||||
meta_window_actor_set_wayland_surface (window_actor, surface);
|
||||
|
||||
surface->window = window;
|
||||
window->surface = surface;
|
||||
|
||||
/* If the window is already meant to have focus then the
|
||||
* original attempt to call this in response to the FocusIn
|
||||
* event will have been lost because there was no surface
|
||||
* yet. */
|
||||
if (window->has_focus)
|
||||
meta_wayland_compositor_set_input_focus (compositor, window);
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
static const struct xserver_interface xserver_implementation = {
|
||||
xserver_set_window_id
|
||||
};
|
||||
|
||||
static void
|
||||
bind_xserver (struct wl_client *client,
|
||||
void *data,
|
||||
guint32 version,
|
||||
guint32 id)
|
||||
{
|
||||
MetaWaylandCompositor *compositor = data;
|
||||
|
||||
/* If it's a different client than the xserver we launched,
|
||||
* don't start the wm. */
|
||||
if (client != compositor->xwayland_client)
|
||||
return;
|
||||
|
||||
compositor->xserver_resource =
|
||||
wl_resource_create (client, &xserver_interface, version, id);
|
||||
wl_resource_set_implementation (compositor->xserver_resource,
|
||||
&xserver_implementation, compositor, NULL);
|
||||
|
||||
wl_resource_post_event (compositor->xserver_resource,
|
||||
XSERVER_LISTEN_SOCKET,
|
||||
compositor->xwayland_abstract_fd);
|
||||
|
||||
wl_resource_post_event (compositor->xserver_resource,
|
||||
XSERVER_LISTEN_SOCKET,
|
||||
compositor->xwayland_unix_fd);
|
||||
|
||||
/* Make sure xwayland will recieve the above sockets in a finite
|
||||
* time before unblocking the initialization mainloop since we are
|
||||
* then going to immediately try and connect to those as the window
|
||||
* manager. */
|
||||
wl_client_flush (client);
|
||||
|
||||
/* At this point xwayland is all setup to start accepting
|
||||
* connections so we can quit the transient initialization mainloop
|
||||
* and unblock meta_wayland_init() to continue initializing mutter.
|
||||
* */
|
||||
g_main_loop_quit (compositor->init_loop);
|
||||
compositor->init_loop = NULL;
|
||||
}
|
||||
|
||||
static char *
|
||||
create_lockfile (int display, int *display_out)
|
||||
{
|
||||
char *filename;
|
||||
int size;
|
||||
char pid[11];
|
||||
int fd;
|
||||
|
||||
do
|
||||
{
|
||||
char *end;
|
||||
pid_t other;
|
||||
|
||||
filename = g_strdup_printf ("/tmp/.X%d-lock", display);
|
||||
fd = open (filename, O_WRONLY | O_CLOEXEC | O_CREAT | O_EXCL, 0444);
|
||||
|
||||
if (fd < 0 && errno == EEXIST)
|
||||
{
|
||||
fd = open (filename, O_CLOEXEC, O_RDONLY);
|
||||
if (fd < 0 || read (fd, pid, 11) != 11)
|
||||
{
|
||||
const char *msg = strerror (errno);
|
||||
g_warning ("can't read lock file %s: %s", filename, msg);
|
||||
g_free (filename);
|
||||
|
||||
/* ignore error and try the next display number */
|
||||
display++;
|
||||
continue;
|
||||
}
|
||||
close (fd);
|
||||
|
||||
other = strtol (pid, &end, 0);
|
||||
if (end != pid + 10)
|
||||
{
|
||||
g_warning ("can't parse lock file %s", filename);
|
||||
g_free (filename);
|
||||
|
||||
/* ignore error and try the next display number */
|
||||
display++;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (kill (other, 0) < 0 && errno == ESRCH)
|
||||
{
|
||||
g_warning ("unlinking stale lock file %s", filename);
|
||||
if (unlink (filename) < 0)
|
||||
{
|
||||
const char *msg = strerror (errno);
|
||||
g_warning ("failed to unlink stale lock file: %s", msg);
|
||||
display++;
|
||||
}
|
||||
g_free (filename);
|
||||
continue;
|
||||
}
|
||||
|
||||
g_free (filename);
|
||||
display++;
|
||||
continue;
|
||||
}
|
||||
else if (fd < 0)
|
||||
{
|
||||
const char *msg = strerror (errno);
|
||||
g_warning ("failed to create lock file %s: %s", filename , msg);
|
||||
g_free (filename);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
while (1);
|
||||
|
||||
/* Subtle detail: we use the pid of the wayland compositor, not the xserver
|
||||
* in the lock file. */
|
||||
size = snprintf (pid, 11, "%10d\n", getpid ());
|
||||
if (size != 11 || write (fd, pid, 11) != 11)
|
||||
{
|
||||
unlink (filename);
|
||||
close (fd);
|
||||
g_warning ("failed to write pid to lock file %s", filename);
|
||||
g_free (filename);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
close (fd);
|
||||
|
||||
*display_out = display;
|
||||
return filename;
|
||||
}
|
||||
|
||||
static int
|
||||
bind_to_abstract_socket (int display)
|
||||
{
|
||||
struct sockaddr_un addr;
|
||||
socklen_t size, name_size;
|
||||
int fd;
|
||||
|
||||
fd = socket (PF_LOCAL, SOCK_STREAM | SOCK_CLOEXEC, 0);
|
||||
if (fd < 0)
|
||||
return -1;
|
||||
|
||||
addr.sun_family = AF_LOCAL;
|
||||
name_size = snprintf (addr.sun_path, sizeof addr.sun_path,
|
||||
"%c/tmp/.X11-unix/X%d", 0, display);
|
||||
size = offsetof (struct sockaddr_un, sun_path) + name_size;
|
||||
if (bind (fd, (struct sockaddr *) &addr, size) < 0)
|
||||
{
|
||||
g_warning ("failed to bind to @%s: %s\n",
|
||||
addr.sun_path + 1, strerror (errno));
|
||||
close (fd);
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (listen (fd, 1) < 0)
|
||||
{
|
||||
close (fd);
|
||||
return -1;
|
||||
}
|
||||
|
||||
return fd;
|
||||
}
|
||||
|
||||
static int
|
||||
bind_to_unix_socket (int display)
|
||||
{
|
||||
struct sockaddr_un addr;
|
||||
socklen_t size, name_size;
|
||||
int fd;
|
||||
|
||||
fd = socket (PF_LOCAL, SOCK_STREAM | SOCK_CLOEXEC, 0);
|
||||
if (fd < 0)
|
||||
return -1;
|
||||
|
||||
addr.sun_family = AF_LOCAL;
|
||||
name_size = snprintf (addr.sun_path, sizeof addr.sun_path,
|
||||
"/tmp/.X11-unix/X%d", display) + 1;
|
||||
size = offsetof (struct sockaddr_un, sun_path) + name_size;
|
||||
unlink (addr.sun_path);
|
||||
if (bind (fd, (struct sockaddr *) &addr, size) < 0)
|
||||
{
|
||||
char *msg = strerror (errno);
|
||||
g_warning ("failed to bind to %s (%s)\n", addr.sun_path, msg);
|
||||
close (fd);
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (listen (fd, 1) < 0) {
|
||||
unlink (addr.sun_path);
|
||||
close (fd);
|
||||
return -1;
|
||||
}
|
||||
|
||||
return fd;
|
||||
}
|
||||
|
||||
static void
|
||||
uncloexec_and_setpgid (gpointer user_data)
|
||||
{
|
||||
int fd = GPOINTER_TO_INT (user_data);
|
||||
|
||||
/* Make sure the client end of the socket pair doesn't get closed
|
||||
* when we exec xwayland. */
|
||||
int flags = fcntl (fd, F_GETFD);
|
||||
if (flags != -1)
|
||||
fcntl (fd, F_SETFD, flags & ~FD_CLOEXEC);
|
||||
|
||||
/* Put this process in a background process group, so that Ctrl-C
|
||||
goes to mutter only */
|
||||
setpgid (0, 0);
|
||||
}
|
||||
|
||||
static void
|
||||
xserver_died (GPid pid,
|
||||
gint status,
|
||||
gpointer user_data)
|
||||
{
|
||||
if (!WIFEXITED (status))
|
||||
g_error ("X Wayland crashed; aborting");
|
||||
else
|
||||
{
|
||||
/* For now we simply abort if we see the server exit.
|
||||
*
|
||||
* In the future X will only be loaded lazily for legacy X support
|
||||
* but for now it's a hard requirement. */
|
||||
g_error ("Spurious exit of X Wayland server");
|
||||
}
|
||||
}
|
||||
|
||||
static int
|
||||
x_io_error (Display *display)
|
||||
{
|
||||
g_error ("Connection to xwayland lost");
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
gboolean
|
||||
meta_xwayland_start (MetaWaylandCompositor *compositor)
|
||||
{
|
||||
int display = 0;
|
||||
char *lockfile = NULL;
|
||||
int sp[2];
|
||||
pid_t pid;
|
||||
char **env;
|
||||
char *fd_string;
|
||||
char *display_name;
|
||||
char *args[11];
|
||||
GError *error;
|
||||
|
||||
wl_global_create (compositor->wayland_display,
|
||||
&xserver_interface, 1,
|
||||
compositor, bind_xserver);
|
||||
|
||||
do
|
||||
{
|
||||
lockfile = create_lockfile (display, &display);
|
||||
if (!lockfile)
|
||||
{
|
||||
g_warning ("Failed to create an X lock file");
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
compositor->xwayland_abstract_fd = bind_to_abstract_socket (display);
|
||||
if (compositor->xwayland_abstract_fd < 0)
|
||||
{
|
||||
unlink (lockfile);
|
||||
|
||||
if (errno == EADDRINUSE)
|
||||
{
|
||||
display++;
|
||||
continue;
|
||||
}
|
||||
else
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
compositor->xwayland_unix_fd = bind_to_unix_socket (display);
|
||||
if (compositor->xwayland_abstract_fd < 0)
|
||||
{
|
||||
unlink (lockfile);
|
||||
close (compositor->xwayland_abstract_fd);
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
while (1);
|
||||
|
||||
compositor->xwayland_display_index = display;
|
||||
compositor->xwayland_lockfile = lockfile;
|
||||
|
||||
/* We want xwayland to be a wayland client so we make a socketpair to setup a
|
||||
* wayland protocol connection. */
|
||||
if (socketpair (AF_UNIX, SOCK_STREAM | SOCK_CLOEXEC, 0, sp) < 0)
|
||||
{
|
||||
g_warning ("socketpair failed\n");
|
||||
unlink (lockfile);
|
||||
return 1;
|
||||
}
|
||||
|
||||
env = g_get_environ ();
|
||||
fd_string = g_strdup_printf ("%d", sp[1]);
|
||||
env = g_environ_setenv (env, "WAYLAND_SOCKET", fd_string, TRUE);
|
||||
g_free (fd_string);
|
||||
|
||||
display_name = g_strdup_printf (":%d",
|
||||
compositor->xwayland_display_index);
|
||||
|
||||
args[0] = XWAYLAND_PATH;
|
||||
args[1] = display_name;
|
||||
args[2] = "-wayland";
|
||||
args[3] = "-rootless";
|
||||
args[4] = "-retro";
|
||||
args[5] = "-noreset";
|
||||
args[6] = "-logfile";
|
||||
args[7] = g_build_filename (g_get_user_cache_dir (), "xwayland.log", NULL);
|
||||
args[8] = "-nolisten";
|
||||
args[9] = "all";
|
||||
args[10] = NULL;
|
||||
|
||||
error = NULL;
|
||||
if (g_spawn_async (NULL, /* cwd */
|
||||
args,
|
||||
env,
|
||||
G_SPAWN_LEAVE_DESCRIPTORS_OPEN |
|
||||
G_SPAWN_DO_NOT_REAP_CHILD |
|
||||
G_SPAWN_STDOUT_TO_DEV_NULL |
|
||||
G_SPAWN_STDERR_TO_DEV_NULL,
|
||||
uncloexec_and_setpgid,
|
||||
GINT_TO_POINTER (sp[1]),
|
||||
&pid,
|
||||
&error))
|
||||
{
|
||||
g_message ("forked X server, pid %d\n", pid);
|
||||
|
||||
close (sp[1]);
|
||||
compositor->xwayland_client =
|
||||
wl_client_create (compositor->wayland_display, sp[0]);
|
||||
|
||||
compositor->xwayland_pid = pid;
|
||||
g_child_watch_add (pid, xserver_died, NULL);
|
||||
}
|
||||
else
|
||||
{
|
||||
g_error ("Failed to fork for xwayland server: %s", error->message);
|
||||
}
|
||||
|
||||
g_strfreev (env);
|
||||
g_free (display_name);
|
||||
|
||||
/* We need to run a mainloop until we know xwayland has a binding
|
||||
* for our xserver interface at which point we can assume it's
|
||||
* ready to start accepting connections. */
|
||||
compositor->init_loop = g_main_loop_new (NULL, FALSE);
|
||||
|
||||
g_main_loop_run (compositor->init_loop);
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
/* To be called right after connecting */
|
||||
void
|
||||
meta_xwayland_complete_init (void)
|
||||
{
|
||||
/* We install an X IO error handler in addition to the child watch,
|
||||
because after Xlib connects our child watch may not be called soon
|
||||
enough, and therefore we won't crash when X exits (and most important
|
||||
we won't reset the tty).
|
||||
*/
|
||||
XSetIOErrorHandler (x_io_error);
|
||||
}
|
||||
|
||||
void
|
||||
meta_xwayland_stop (MetaWaylandCompositor *compositor)
|
||||
{
|
||||
char path[256];
|
||||
|
||||
snprintf (path, sizeof path, "/tmp/.X%d-lock",
|
||||
compositor->xwayland_display_index);
|
||||
unlink (path);
|
||||
snprintf (path, sizeof path, "/tmp/.X11-unix/X%d",
|
||||
compositor->xwayland_display_index);
|
||||
unlink (path);
|
||||
|
||||
unlink (compositor->xwayland_lockfile);
|
||||
}
|
||||
792
src/wayland/weston-launch.c
Normal file
792
src/wayland/weston-launch.c
Normal 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;
|
||||
}
|
||||
69
src/wayland/weston-launch.h
Normal file
69
src/wayland/weston-launch.h
Normal 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
|
||||
@@ -205,11 +205,14 @@
|
||||
|
||||
Changes the backlight of @output to @value, which is
|
||||
expressed as a percentage and rounded to the HW limits.
|
||||
|
||||
Returns the new value after rounding.
|
||||
-->
|
||||
<method name="ChangeBacklight">
|
||||
<arg name="serial" direction="in" type="u" />
|
||||
<arg name="output" direction="in" type="u" />
|
||||
<arg name="value" direction="in" type="i" />
|
||||
<arg name="new_value" direction="out" type="i" />
|
||||
</method>
|
||||
|
||||
<!--
|
||||
@@ -277,5 +280,14 @@
|
||||
XRandR interface directly).
|
||||
-->
|
||||
<property name="PowerSaveMode" type="i" access="readwrite" />
|
||||
|
||||
<!--
|
||||
MonitorsChanged:
|
||||
|
||||
The signal is emitted every time the screen configuration
|
||||
changes.
|
||||
The client should then call GetResources() to read the new layout.
|
||||
-->
|
||||
<signal name="MonitorsChanged" />
|
||||
</interface>
|
||||
</node>
|
||||
|
||||
Reference in New Issue
Block a user