Compare commits

...

97 Commits

Author SHA1 Message Date
10fbaf7695 shaped-texture: Turn blending off when drawing entirely opaque regions
When drawing entirely opaque regions, we traditionally kept blending on
simply because it made the code more convenient and obvious to handle.
However, this can cause lots of performance issues on GPUs that aren't
too powerful, as they have to readback the buffer underneath.

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

https://bugzilla.gnome.org/show_bug.cgi?id=706930
2013-08-28 13:23:09 -04:00
ef73402654 shaped-texture: Use non-deprecated cogl APIs
https://bugzilla.gnome.org/show_bug.cgi?id=706930
2013-08-28 13:23:09 -04:00
22cdf2d650 shaped-texture: Simplify pipeline creation
Split out pipeline creation to a separate function so that we don't
have so much dense code in the paint function itself, and remove some
indentation levels.

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

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

https://bugzilla.gnome.org/show_bug.cgi?id=706930
2013-08-28 13:23:08 -04:00
284b497b4c wayland: Add support for set_opaque_region / set_input_region 2013-08-28 13:18:04 -04:00
7d9141c56f Revert "MetaWaylandKeyboard: use the new clutter-evdev support for xkbcommon"
This reverts commit 519a06b93d.
Depends on unreviewed stuff and breaks the build.
2013-08-28 18:04:01 +02:00
519a06b93d MetaWaylandKeyboard: use the new clutter-evdev support for xkbcommon
We need to track the full xkb_state to have the necessary information
to send to the clients, otherwise they may get confused and lock
or invert the modifiers. In the evdev backend, we just retrieve the
same state object that clutter is using, while in the other backends
we fake the state using what clutter is providing (which is a subset
of what X11 provides, which would be necessary to have full state)

https://bugzilla.gnome.org/show_bug.cgi?id=705862
2013-08-28 17:45:25 +02:00
0cceddab75 MetaWayland: install an X io error handler
This way can detect X disconnections correctly, crash with a core
dump and reset the tty.

https://bugzilla.gnome.org/show_bug.cgi?id=706962
2013-08-28 17:21:18 +02:00
c9830c13b4 screen: Remove unused variable 2013-08-27 08:56:06 -04:00
9a4783e364 Integrate the monitor manager with wayland
Use the right backend when running as a wayland compositor,
export the data to wayland clients, and use it to keep the stage
appropriately sized.
2013-08-27 10:09:39 +02:00
aa15c09d54 Merge tag 'xrandr_branch_point' into wayland-kms-base
Conflicts:
	src/Makefile.am
	src/core/display.c
	src/core/screen-private.h
	src/core/screen.c
2013-08-27 10:07:39 +02:00
62d908be42 MonitorManager: return the new backlight after changing
Modify the interface of ChangeBacklight to return the new value,
to account for rounding to HW limits.

https://bugzilla.gnome.org/show_bug.cgi?id=706729
2013-08-27 09:58:50 +02:00
6526e9882b idle-monitor: Fix a warning when a callback removes the user active watch
The user active watch is a one-fire watch, but it is valid in the API
for the callback to explicitly remove the watch itself. In that case,
the watch will be invalid after the user removes it, and the memory
potentially freed. So make sure to not dereference the watch after
the callback is called.

https://bugzilla.gnome.org/show_bug.cgi?id=706825
2013-08-27 09:57:06 +02:00
ab72352c47 background: don't save pixbuf in user data
https://bugzilla.gnome.org/show_bug.cgi?id=706777
2013-08-27 09:57:06 +02:00
f09b9573f0 window: ignore skip-taskbar hint on parentless dialogs
Dialogs that don't have a parent should not be skip-taskbar,
otherwise they get lost and there is no way to recover them
(because they're not autoraised when activating the parent),
but toolkits and applications set the hint anyway.

https://bugzilla.gnome.org/show_bug.cgi?id=673399
2013-08-27 09:57:06 +02:00
3d3ae40f79 Bump version to 3.9.90
gnome-shell requires mutter and mutter-wayland at the same version
to build. Also update NEWS, cherry-picked from master.
2013-08-27 09:51:56 +02:00
ea3d2b4759 wayland: make parallel installable with regular mutter
Modify all visible instances of mutter with mutter-wayland
(libraries, folders, pkgconfig, etc.), so that the wayland
branch can be installed alongside the usual X11 mutter.

https://bugzilla.gnome.org/show_bug.cgi?id=705497
2013-08-26 15:00:29 +02:00
542a0886cf Remove files no one cares about anymore
mutter-plugins.pc has been replaced by libmutter.pc, and the
wm properties in the control center are long gone.

https://bugzilla.gnome.org/show_bug.cgi?id=705497
2013-08-26 15:00:29 +02:00
bd3d5df9ce Remove HAVE_WAYLAND ifdefs
Wayland support is always enabled now.

https://bugzilla.gnome.org/show_bug.cgi?id=705497
2013-08-26 15:00:29 +02:00
91cdfab495 build: make wayland support unconditional
In the wayland branch of mutter, we want to build a wayland version
of the mutter libraries, and that's much easier if we just build
wayland support unconditionally.
The define is kept to avoid a huge diff, but should be removed
in a later patch.
Also, wayland support can still be disable at runtime, by
launching mutter without the --nested switch.

https://bugzilla.gnome.org/show_bug.cgi?id=705497
2013-08-26 15:00:29 +02:00
1617323dca MetaShapedTexture: don't include private headers in public ones
Private headers are not installed, so they can't be referenced
from public ones.

https://bugzilla.gnome.org/show_bug.cgi?id=705497
2013-08-26 15:00:29 +02:00
bbf9358eba window: Fix a compiler warning 2013-08-23 23:09:58 -04:00
9682a2aea4 window-group: Fix compiler warning 2013-08-23 22:11:37 -04:00
c9fbb51775 wayland: Fix build when building without Wayland 2013-08-23 22:05:11 -04:00
aa6d887214 wayland-private: Ignore deprecations around legacy_buffer
yes, it's deprecated, that's why it's marked legacy. now shush.
2013-08-23 22:00:52 -04:00
13312527de Update .gitignore 2013-08-23 18:23:33 -04:00
9bd366f2a6 MonitorXrandr: skip CRTC reconfigurations that have no effect
If we're attempting to reconfigure the CRTCs to the same parameter,
skip the X call, as in some drivers a modeset can take time and
cause flicker.

https://bugzilla.gnome.org/show_bug.cgi?id=706672
2013-08-23 18:06:02 +02:00
5de346bfef MonitorXrandr: Fix segv when accessing possible_clones
This code requires a double pass and the segv happens later
when trying to iterate over the array.

https://bugzilla.gnome.org/show_bug.cgi?id=706598
2013-08-23 16:40:43 +02:00
2af20b77b6 MonitorXrandr: fix reading the current DPMS level
Add missing break statements, to avoid falling always through
to the invalid case.

https://bugzilla.gnome.org/show_bug.cgi?id=706582
2013-08-23 16:40:43 +02:00
9affbf10a6 MetaIdleMonitor: add wayland support
Keep a timer source that we reset when we capture an event in
MetaWayland, and fire watches accordingly.

https://bugzilla.gnome.org/show_bug.cgi?id=706005
2013-08-23 16:34:02 +02:00
c0acf3ae6d MetaIdleMonitor: add a DBus interface for the idle monitor
To allow other clients (gnome-session, gnome-settings-daemon)
to monitor user activity, introduce a DBus interface for the
idle monitor inside mutter.

https://bugzilla.gnome.org/show_bug.cgi?id=706005
2013-08-23 16:34:02 +02:00
2c1b20e15f Add a new helper for tracking user idle activity
When running as a wayland compositor, we can't use the xserver's
IDLETIME, because that's updated only in response to X events.
But we have all the events ourselves, so we can just run the timer
in process.

https://bugzilla.gnome.org/show_bug.cgi?id=706005
2013-08-23 16:34:02 +02:00
77290b6736 Update .gitignore 2013-08-20 17:04:43 -04:00
876f81db12 compositor: fix focusing the stage window
We can't use the X11 stage window, if clutter is not using the X11
backend (and even if it was, it would be bogus when the xwayland
server is not the one clutter is talking to). Instead, we introduce
the concept of "focus type", which we use to differentiate the
various meanings of None in the focus_xwindow field.

https://bugzilla.gnome.org/show_bug.cgi?id=706364
2013-08-20 14:41:24 +02:00
75f3ae14b5 MetaPlugin: simplify the modal API
Remove grab window and cursor from the API, and just grab always
on the stage window with no cursor.
This is mainly to remove the X11 usage in the public API, in preparation
for implementing this in wayland.

https://bugzilla.gnome.org/show_bug.cgi?id=705917
2013-08-20 14:25:49 +02:00
d26f248b0f wayland: fix a compiler warning
Implicit declaration of memset

https://bugzilla.gnome.org/show_bug.cgi?id=706363
2013-08-20 14:13:49 +02:00
eeb3dfc991 MonitorManager: emit a DBus signal when we change the display configuration
Using out-of-band notifications from the wayland protocol or from
X is racy, in that the client could ask for the new resources before
we have them.
Instead, with a signal, we are sure that when the client asks for
it, it will get the right values.

https://bugzilla.gnome.org/show_bug.cgi?id=706382
2013-08-20 14:08:50 +02:00
e66db2eab3 MonitorManager: extend the API with physical sizes
These will be needed in the new display panel designs to show
the diagonal length and physical aspect ratio.

https://bugzilla.gnome.org/show_bug.cgi?id=706322
2013-08-20 14:08:50 +02:00
2ae7454f36 Add MetaCursorTracker, a new helper for tracking the cursor sprite
Under X, we need to use XFixes to watch the cursor changing, while
on wayland, we're in charge of setting and painting the cursor.
MetaCursorTracker provides the abstraction layer for gnome-shell,
which can thus drop ShellXFixesCursor. In the future, it may grow
the ability to watch for pointer position too, especially if
CursorEvents are added to the next version of XInput2, and thus
it would also replace the PointerWatcher we use for gnome-shell's
magnifier.

https://bugzilla.gnome.org/show_bug.cgi?id=705911
2013-08-19 16:09:53 +02:00
4fd3c63da9 ui: restrict gtk to only use the x11 backend
We use GTK as a way to get the X11 connection for our internal
use, so we need it to keep using X.

https://bugzilla.gnome.org/show_bug.cgi?id=706303
2013-08-19 15:42:37 +02:00
909a6607c5 MonitorXrandr: try harder to get decent product/serial IDs
If the EDID does not include free-form product name and serial
number, use the numeric IDs instead, like gnome-desktop did.

https://bugzilla.gnome.org/show_bug.cgi?id=706233
2013-08-19 09:45:31 +02:00
1bde397edf Updated POTFILES.in 2013-08-18 22:03:23 +02:00
015c05fbf6 MonitorXrandr: fix setting gamma ramps
The value passed to XRRCrtcSetGamma must be allocated with
XRRAllocGamma (because it relies on the locations of green and blue),
otherwise garbage is sent on the wire.

https://bugzilla.gnome.org/show_bug.cgi?id=706231
2013-08-18 12:11:42 +02:00
8ad5ccd2f8 MonitorConfig: switch to the real configuration file
Forgot to do before pushing...
2013-08-18 01:10:00 +02:00
115cc870c7 build: Fix srcdir != builddir 2013-08-17 19:05:50 -04:00
3112794d83 MonitorXrandr: update the internal data structures after applying
We were relying on the XRandR events from the X server to update
the configuration, but we were calling meta_monitor_config_update_current()
immediately after, so the MonitorConfig would be updated with the
old configuration (and we would save that to disk!)

https://bugzilla.gnome.org/show_bug.cgi?id=705670
2013-08-18 00:48:31 +02:00
3528b067d0 MonitorXrandr: follow the right order in applying the new configuration
First disable CRTCs that should be off in the new configuration,
then resize the framebuffer, then enable the new CRTCs.
If we don't do that, and we're making the screen smaller, X complains
with a BadMatch.

https://bugzilla.gnome.org/show_bug.cgi?id=705670
2013-08-18 00:47:53 +02:00
0986b660be MonitorXrandr: resize the framebuffer prior to setting the CRTC configuration
Otherwise X11 will trim the new configuration and disable outputs
outside the screen.

https://bugzilla.gnome.org/show_bug.cgi?id=705670
2013-08-18 00:47:53 +02:00
3bb5086173 Monitor: restore correct display name handling
Now that we have the right values from the EDID, we can load
the PNP database and find the proper vendor name, to show in
the control center UI.

https://bugzilla.gnome.org/show_bug.cgi?id=705670
2013-08-18 00:47:53 +02:00
69467842ab MonitorXrandr: implement correct EDID parsing
To provide valid values for the vendor, product and serial fields
we need to read the EDID and parse it.
Parser kindly provided by gnome-desktop.

https://bugzilla.gnome.org/show_bug.cgi?id=705670
2013-08-18 00:47:53 +02:00
57077435ed MonitorManager: add EDID properties to the output DBus description
Add "edid-file", if we have one (in the KMS case, where we can point
people to the right sysfs file), or "edid" with inline data.
These are needed by colord to build the default ICC profile for
uncalibrated displays.

https://bugzilla.gnome.org/show_bug.cgi?id=705670
2013-08-18 00:47:53 +02:00
46de0ed462 MonitorManager: split the XRandR parts in a subclass
Instead of keeping a forest of if backend else ..., use a subclass
and virtual functions to discriminate between XRandR and the
dummy backend (which lives in the parent class togheter with the
common code)

https://bugzilla.gnome.org/show_bug.cgi?id=705670
2013-08-18 00:47:53 +02:00
5086626805 MetaPlugin: add a UI hook for confirming display changes
We want to show a dialog when a display change happens from the
control center. To do so, add a new vfunc to MetaPlugin and
call it when a configuration change is requested via DBus.

https://bugzilla.gnome.org/show_bug.cgi?id=705670
2013-08-18 00:47:53 +02:00
bbbcd8c631 MonitorConfig: handle changes in the laptop lid
This way we don't need to track the current and previous
configuration in gnome-settings-daemon, when we already do so
in mutter.

https://bugzilla.gnome.org/show_bug.cgi?id=705670
2013-08-18 00:47:53 +02:00
3b61b85f2c MonitorManager: add gamma support
Add GetCrtcGamma() and SetCrtcGamma(), that wrap the similarly
named XRandR API. These are used by GnomeRR inside the color
plugin of the control center (and may go away if the color
plugin decides to do something different under wayland)

https://bugzilla.gnome.org/show_bug.cgi?id=705670
2013-08-18 00:47:53 +02:00
cd20f1bc0b MonitorManager: ignore configuration changes that disable all outputs
If we compute a screen size of 0 in either direction, we crash
later on, as it is invalid for clutter, cogl and X.

https://bugzilla.gnome.org/show_bug.cgi?id=705670
2013-08-18 00:47:53 +02:00
8b52782ed4 MonitorManager: add support for backlight
GnomeRR needs that too.
The backlight is exported as a normalized 0-100 value, or -1 if not
supported. Clamping to HW limits is handled by the backend.
Changing backlight uses a different method call, to avoid recomputing
the full display configuration every time the user presses the
backlight keys.

https://bugzilla.gnome.org/show_bug.cgi?id=705670
2013-08-18 00:47:53 +02:00
849050be95 MonitorManager: further extend the dummy backend
The default configuration is extended, which is only possible
if there are as many CRTCs as outputs, so make sure that's true.

Also, add more and bigger modes, so that different sizes will
be chosen for the three outputs.
A nice side effect of this is that with a real 1920x1080 + 1600x900
layout, if you disable the VGA you get a stage that matches the
screen size, which triggers the legacy fullscreen path in the
outside mutter.

https://bugzilla.gnome.org/show_bug.cgi?id=705670
2013-08-18 00:47:52 +02:00
5c27a91684 MonitorManager: store the presentation mode bit in XRandR
Use a private output property to store if the output is in
presentation mode or not, so that this information is not lost
after the configuration read back from the server.

https://bugzilla.gnome.org/show_bug.cgi?id=705670
2013-08-18 00:34:16 +02:00
764c472edb MonitorConfig: add support for default configurations
Activate the presentation bit on new hotplugged monitors, while
making a fully extended setup when running for the first time.

https://bugzilla.gnome.org/show_bug.cgi?id=705670
2013-08-18 00:34:16 +02:00
d0529b7482 MonitorConfig: add CRTC assignment
Ripped off libgnome-desktop, trimming the parts that checked
that the configuration was plausible, as that should be done
in gnome-control-center before asking mutter for a change.

https://bugzilla.gnome.org/show_bug.cgi?id=705670
2013-08-18 00:34:15 +02:00
8f4621240a MonitorManager: add support for persistent monitor configurations
Add a new object, MetaMonitorConfig, that takes care of converting
between the logical configurations stored in monitors.xml and
the HW resources exposed by MonitorManager.
This commit includes loading and saving of configurations, but
still missing is the actual CRTC assignments and a default
configuration when none is found in the file.

https://bugzilla.gnome.org/show_bug.cgi?id=705670
2013-08-18 00:33:37 +02:00
e039add240 MonitorManager: add support for DPMS levels
To the XRandR and dummy backend (and as usual the dummy backend
has no effect)

https://bugzilla.gnome.org/show_bug.cgi?id=705670
2013-08-18 00:33:37 +02:00
dbd8d4d598 MonitorManager: inherit directly from DisplayConfig instead of handling signals
This way we can handle properties too.

https://bugzilla.gnome.org/show_bug.cgi?id=705670
2013-08-18 00:33:37 +02:00
522542c486 MonitorManager: fix handling of output transform
Read the current transform from XRandR, and expose the transforms
that are really supported on the bus.
The dummy backend now advertises all transforms, since it doesn't
actually apply them.

https://bugzilla.gnome.org/show_bug.cgi?id=705670
2013-08-18 00:33:35 +02:00
fc67c707e4 default plugin: add a random color background on each monitor
Instead of a full white background, make one with a random color.
This way the different "monitors" are visible and it's easier
to debug the DBus API.

https://bugzilla.gnome.org/show_bug.cgi?id=705670
2013-08-18 00:32:44 +02:00
c354e7e81b DisplayConfig: make the dummy backend writable
Add a number of dummy outputs and modes to the dummy backend,
and implement the writing bits.
The only visible effect is that you can change the screen size,
which resizes the output window.

https://bugzilla.gnome.org/show_bug.cgi?id=705670
2013-08-18 00:32:43 +02:00
bf40409d97 Reverse handling of XRandR events between Screen and MonitorManager
Now MonitorManager does its own handling of XRandR events, which
means we no longer handle ConfigureNotify on the root window.
MetaScreen reacts to MonitorManager::monitor-changed and updates
its internal state, including the new size.

This paves the way for doing display configuration using only
the dummy backend, which would allow testing wl_output interfaces.

https://bugzilla.gnome.org/show_bug.cgi?id=705670
2013-08-18 00:31:10 +02:00
57d083730e DisplayConfig: add the write side of the API
Implement ApplyConfiguration in terms of XRandR calls.
Error checking is done before actually committing the configuration.

If mutter is using one of the other monitor config backends, an
error is reported and nothing happens.

https://bugzilla.gnome.org/show_bug.cgi?id=705670
2013-08-18 00:31:09 +02:00
dc242e46c2 Extend the DBus XRandR protocol to expose cloning restriction
Turns out that even if two outputs say that they can be controlled
by a given CRTC, you can't configure them in the same CRTC unless
they are marked as "possible clones" one of the other.
This can further restrict the configuration options, so we need
to expose this limitation in the DBus API.

https://bugzilla.gnome.org/show_bug.cgi?id=705670
2013-08-18 00:22:54 +02:00
7e1d1003c9 Add the write side of the DBus protocol too
This is just in the documentation for now, to attract wider feedback
before we start looking at how to implement this for real.

https://bugzilla.gnome.org/show_bug.cgi?id=705670
2013-08-18 00:22:54 +02:00
3bb33d384f Introduce a new DBus interface for display configuration
This new interface will be used by the control center and possibly
the settings daemon to configure the screens. It is designed to
resemble a simplified XRandR, while still exposing all the quirks
of the hardware, so that the panel can limit the user choices
appropriately.

To do so, MetaMonitorMode needs to track CRTCs, outputs and modes,
so the low level objects have been decoupled from the high-level
MetaMonitorInfo, which is used by core and API and offers a simplified
view of HW, that hides away the details of what is cloned and how.
This is still not efficient as it should be, because on every
HW change we drop all data structures and rebuild them from scratch
(which is not expensive because there aren't many of them, but
at least in the XRandR path it involves a few sync X calls)

https://bugzilla.gnome.org/show_bug.cgi?id=705670
2013-08-18 00:22:54 +02:00
214f31257b Rework and consolidate monitor handling in MetaScreen
Consolidate all places that deal with output configuration in
MetaScreen, which gets it either from XRandR or from a dummy static configuration.
We still need to read the Xinerama config, even when running xwayland,
because we need the indices for _NET_WM_FULLSCREEN_MONITORS, but
now we do it only when needed.

https://bugzilla.gnome.org/show_bug.cgi?id=705670
2013-08-18 00:22:54 +02:00
bfc87d13cb MetaWindowActor: fix reference counting issue
We need to use g_signal_connect_object(), rather than g_signal_connect(),
because the window actor can be destroyed before the window emits
the final notify::appears-focused inside unmanage, if the plugin
decides that it doesn't want to animate the destruction (which
happens with dialogs and the default plugin)

https://bugzilla.gnome.org/show_bug.cgi?id=706207
2013-08-18 00:22:07 +02:00
506ddc3d6c MetaWindowActor: fix reference counting issue
We need to use g_signal_connect_object(), rather than g_signal_connect(),
because the window actor can be destroyed before the window emits
the final notify::appears-focused inside unmanage, if the plugin
decides that it doesn't want to animate the destruction (which
happens with dialogs and the default plugin)

https://bugzilla.gnome.org/show_bug.cgi?id=706207
2013-08-18 00:16:59 +02:00
24564c77d6 build: Fix srcdir != builddir
Need to ensure the wayland/ directory exists in $(builddir), and find
the headers there too.
2013-08-17 18:03:10 -04:00
18a21b67c2 wayland: move XWayland support code to its own file
Given that xwayland code is already split in meta-xwayland, it
makes sense to have there the implementation of the private
xserver protocol too.

https://bugzilla.gnome.org/show_bug.cgi?id=705816
2013-08-15 17:42:19 +02:00
3803fd9511 wayland: don't use fork() and SIGCHLD to spawn processes
It is a very bad idea in a glib program (especially one heavily
using glib child watching facilities, like gnome-shell) to handle
SIGCHLD. While we're there, let's also use g_spawn_async, which
solves some malloc-after-fork problems and makes the code generally
cleaner.

https://bugzilla.gnome.org/show_bug.cgi?id=705816
2013-08-15 17:41:34 +02:00
152d896f75 MetaWindowActor/wayland: make sure the mapped bit is correctly set
Otherwise it stays FALSE for OR windows, causing the shape region
to be empty and the actor culled from drawing.

https://bugzilla.gnome.org/show_bug.cgi?id=706078
2013-08-15 17:41:34 +02:00
2f3a5f2001 MetaWaylandSeat: correct logic to set the current / focused surface
The current surface refers to the surface right below the pointer
(according to the pick performed by clutter), while the focus surface
is the one receiving events. They can be out of sync in case of
grabs, in which case we should keep trying to focus the current
surface.

https://bugzilla.gnome.org/show_bug.cgi?id=706077
2013-08-15 17:41:34 +02:00
0e098249b1 Merge remote-tracking branch 'origin/master' into wayland 2013-08-13 10:44:09 -04:00
9a5f243f73 wayland: refactor window destruction and focus
The previous code was leaving focus fields dirty in MetaWaylandPointer
and MetaWaylandKeyboard at time (which could crash the X server
because of invalid object IDs)
The new code is more tighly integrated in the normal X11 code
for handling keyboard focus (meaning that the core idea of input
focus is also correct now), so that meta_window_unmanage() can
do the right thing. As a side benefit, clicking on wayland clients
now unfocus X11 clients.
For the mouse focus, we need to clear the surface pointer when
the metawindowactor is destroyed (even if the actual actor is
kept alive for effects), so that a repick finds a different pointer
focus.

https://bugzilla.gnome.org/show_bug.cgi?id=705859
2013-08-13 09:42:55 +02:00
03f55b9485 wayland: fix a #warning
Remove window_surfaces, as the FIXME asks for. We don't need it
because we can obtain the surface from the MetaWindow, and
follow the wayland compositor path for both types of clients.

https://bugzilla.gnome.org/show_bug.cgi?id=705818
2013-08-13 09:42:55 +02:00
ef9ef87d91 configure: Complete fix for removing clutter-wayland-compositor 2013-08-12 14:43:23 -04:00
0ee2c21da7 meta-wayland: Remove get_surface_rect
It's the same as get_input_rect, which we already have in window.
2013-08-12 12:13:45 -04:00
9b966561c4 configure.ac: Remove clutter-wayland-compositor dependency
It's not necessary.
2013-08-12 12:08:58 -04:00
8c0779a9db wayland: add missing file
A Makefile.am is required for the protocol/ directory

https://bugzilla.gnome.org/show_bug.cgi?id=705815
2013-08-12 09:49:17 +02:00
2c901cc015 wayland: implement shell surface move interface
This implements the shell surface move interface so now it's possible to
use the mouse to interactively move wayland based windows around the
screen.
2013-08-10 19:13:49 -04:00
85e66f69fa wayland: support left click to raise wayland surfaces
This adds support for raising wayland surfaces when clicked with the
left mouse button.
2013-08-10 19:13:49 -04:00
a5585327dc wayland: Add an actor for the cursor
When running Mutter under Cogl's KMS backend no cursor will be
provided so instead this makes it so the cursor will be painted as a
CoglTexture that gets moved in response to mouse motion events. The
painting is done in a subclass of ClutterStage so that we can
guarantee that the cursor will be painted on top of everything else.

This patch adds support for the set_cursor method on the pointer
interface so that clients can change the cursor image.

The set_pointer method sets a surface and a hotspot position to use
for the cursor image. The surface's buffer is converted to a
CoglTexture and attached to a pipeline to paint directly via Cogl. If
a new buffer is attached to the surface the image will be updated. The
cursor reverts back to the default image whenever to the pointer focus
is moved off of any surface.

The image for the pointer is taken from X. It gets installed into
a fixed data location for mutter.
2013-08-10 19:13:49 -04:00
268ebb1b18 wayland: Add basic input support
This copies the basic input support from the Clayland demo compositor.
It adds a basic wl_seat implementation which can convert Clutter mouse
events to Wayland events. For this to work all of the wayland surface
actors need to be made reactive.

The wayland keyboard input focus surface is updated whenever Mutter
sees a FocusIn event so that it will stay in synch with whatever
surface Mutter wants as the focus. Wayland surfaces don't get this
event so for now it will just give them focus whenever they are
clicked as a hack to test the code.

Authored-by: Neil Roberts <neil@linux.intel.com>
Authored-by: Giovanni Campagna <gcampagna@src.gnome.org>
2013-08-10 19:13:49 -04:00
40e820f551 Add support for stacking X and Wayland windows together
This breaks down the assumptions in stack-tracker.c and stack.c that
Mutter is only stacking X windows.

The stack tracker now tracks windows using a MetaStackWindow structure
which is a union with a type member so that X windows can be
distinguished from Wayland windows.

Some notable changes are:

Queued stack tracker operations that affect Wayland windows will not be
associated with an X serial number.

If an operation only affects a Wayland window and there are no queued
stack tracker operations ("unvalidated predictions") then the operation
is applied immediately since there is no server involved with changing
the stacking for Wayland windows.

The stack tracker can no longer respond to X events by turning them into
stack operations and discarding the predicted operations made prior to
that event because operations based on X events don't know anything
about the stacking of Wayland windows.

Instead of discarding old predictions the new approach is to trust the
predictions but whenever we receive an event from the server that
affects stacking we cross-reference with the predicted stack and check
for consistency. So e.g. if we have an event that says ADD window A then
we apply the predictions (up to the serial for that event) and verify
the predicted state includes a window A. Similarly if an event says
RAISE_ABOVE(B, C) we can apply the predictions (up to the serial for
that event) and verify that window B is above C.

If we ever receive spurious stacking events (with a serial older than we
would expect) or find an inconsistency (some things aren't possible to
predict from the compositor) then we hit a re-synchronization code-path
that will query the X server for the full stacking order and then use
that stack to walk through our combined stack and force the X windows to
match the just queried stack but avoiding disrupting the relative
stacking of Wayland windows. This will be relatively expensive but
shouldn't be hit for compositor initiated restacking operations where
our predictions should be accurate.

The code in core/stack.c that deals with synchronizing the window stack
with the X server had to be updated quite heavily. In general the patch
avoids changing the fundamental approach being used but most of the code
did need some amount of re-factoring to consider what re-stacking
operations actually involve X or not and when we need to restack X
windows we sometimes need to search for a suitable X sibling to restack
relative too since the closest siblings may be Wayland windows.
2013-08-10 19:13:49 -04:00
f9a11b3b18 wayland: Adds basic hybrid X + Wayland support
This adds support for running mutter as a hybrid X and Wayland
compositor. It runs a headless XWayland server for X applications
that presents wayland surfaces back to mutter which mutter can then
composite.

This aims to not break Mutter's existing support for the traditional X
compositing model which means a single build of Mutter can be
distributed supporting the traditional model and the new Wayland based
compositing model.

TODO: although building with --disable-wayland has at least been tested,
I still haven't actually verified that running as a traditional
compositor isn't broken currently.

Note: At this point no input is supported

Note: multiple authors have contributed to this patch:
Authored-by: Robert Bragg <robert@linux.intel.com>
Authored-by: Neil Roberts <neil@linux.intel.com>
Authored-by: Rico Tzschichholz.
Authored-by: Giovanni Campagna <gcampagna@src.gnome.org>
2013-08-10 19:13:48 -04:00
bd3c357212 Adds a --nested option
This adds a --nested option to request that mutter no longer run as a
classic X compositor with an output window mapped on the X Composite
Overlay Window and also not assume it is running directly under X.

The intention is that in this mode Mutter will itself launch a headless
X server and display output will be handled by Clutter and Cogl. This
will enable running Mutter nested as an application within an X session.

This patch introduces an internal meta_is_wayland_compositor() function
as a means to condition the way mutter operates when running as a
traditional X compositor vs running as a wayland compositor where the
compositor and display server are combined into a single process.

Later we also expect to add a --kms option as another way of enabling
this wayland compositor mode that will assume full control of the
display hardware instead of running as a nested application.
2013-08-10 19:13:48 -04:00
b4d108dac6 configure: Adds --with-xwayland-path option
This adds a --with-xwayland-path configure option that can be used to
specify the absolute path of a headless X server binary supporting
the wayland xserver protocol.
2013-08-10 19:13:48 -04:00
6585a5760b configure: Adds --enable-wayland config option
This adds a --enable-wayland configure option to enable building mutter
as a hybrid X and Wayland compositor. By default the option is disabled.
If enabled then HAVE_WAYLAND is defined for C code and as an automake
conditional.

This copies the xserver.xml wayland protocol into a protocol/ directory
since wayland support will depend on this protocol for communicating
with an xwayland X server. Copying the spec like this is consistent with
Weston so we don't need a configure option to locate an external spec.
2013-08-10 19:13:48 -04:00
531be6c413 Track the X Shape input region and use it for picking
We now track whether a window has an input shape specified via the X
Shape extension. Intersecting that with the bounding shape (as required
by the X Shape extension) we use the resulting rectangles to paint
window silhouettes when picking. As well as improving the correctness of
picking this should also be much more efficient because typically when
only picking solid rectangles then the need to actually render and issue
a read_pixels request can be optimized away and instead the picking is
done on the cpu.
2013-08-10 19:13:48 -04:00
f0c503b5a9 hack autogen to allow automake 1.13 2013-08-10 19:13:48 -04:00
81 changed files with 16004 additions and 1687 deletions

10
.gitignore vendored
View File

@ -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,10 +46,10 @@ POTFILES
po/*.pot
50-metacity-desktop-key.xml
50-metacity-key.xml
libmutter.pc
mutter
libmutter-wayland.pc
mutter-wayland
mutter-theme-viewer
mutter.desktop
mutter-wayland.desktop
org.gnome.mutter.gschema.valid
org.gnome.mutter.gschema.xml
testasyncgetprop
@ -74,6 +74,8 @@ 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
doc/reference/*.args
doc/reference/*.bak

View File

@ -1,5 +1,5 @@
SUBDIRS=src po doc
SUBDIRS=src protocol data po doc
EXTRA_DIST = HACKING MAINTAINERS rationales.txt

26
NEWS
View File

@ -1,3 +1,29 @@
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]

View File

@ -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) || {

View File

@ -2,7 +2,7 @@ AC_PREREQ(2.50)
m4_define([mutter_major_version], [3])
m4_define([mutter_minor_version], [9])
m4_define([mutter_micro_version], [5])
m4_define([mutter_micro_version], [90])
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])
@ -75,6 +79,8 @@ MUTTER_PC_MODULES="
xcomposite >= 0.2 xfixes xrender xdamage xi >= 1.6.0
$CLUTTER_PACKAGE >= 1.14.3
cogl-1.0 >= 1.13.3
upower-glib > 0.9.11
gnome-desktop-3.0
"
GLIB_GSETTINGS
@ -113,6 +119,21 @@ 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
@ -196,6 +217,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"
PKG_CHECK_MODULES(MUTTER, $MUTTER_PC_MODULES)
PKG_CHECK_EXISTS([xi >= 1.6.99.1],
@ -433,9 +464,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
])
@ -451,7 +483,7 @@ fi
dnl ==========================================================================
echo "
mutter-$VERSION
mutter-wayland-$VERSION
prefix: ${prefix}
source code location: ${srcdir}

3
data/Makefile.am Normal file
View File

@ -0,0 +1,3 @@
defaultcursordir = $(pkgdatadir)/cursors
dist_defaultcursor_DATA = left_ptr.png

BIN
data/left_ptr.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 736 B

View File

@ -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

View File

@ -12,6 +12,7 @@ src/core/display.c
src/core/errors.c
src/core/keybindings.c
src/core/main.c
src/core/monitor.c
src/core/mutter.c
src/core/prefs.c
src/core/screen.c

1
protocol/Makefile.am Normal file
View File

@ -0,0 +1 @@
EXTRA_DIST = xserver.xml

18
protocol/xserver.xml Normal file
View 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>

View File

@ -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,11 +30,21 @@ INCLUDES= \
-DMUTTER_PLUGIN_DIR=\"@MUTTER_PLUGIN_DIR@\" \
-DGETTEXT_PACKAGE=\"$(GETTEXT_PACKAGE)\"
mutter_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/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 \
@ -62,6 +73,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 \
@ -94,6 +106,8 @@ libmutter_la_SOURCES = \
ui/draw-workspace.h \
core/edge-resistance.c \
core/edge-resistance.h \
core/edid-parse.c \
core/edid.h \
core/errors.c \
meta/errors.h \
core/frame.c \
@ -110,6 +124,15 @@ 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 \
core/monitor-private.h \
core/monitor-xrandr.c \
core/mutter-Xatomtype.h \
core/place.c \
core/place.h \
@ -158,8 +181,24 @@ 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
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
@ -178,6 +217,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 \
@ -195,16 +236,16 @@ 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
if HAVE_INTROSPECTION
include $(INTROSPECTION_MAKEFILE)
@ -226,15 +267,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
@ -245,22 +286,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 \
@ -268,6 +304,8 @@ xml_in_files = \
50-mutter-windows.xml.in
xml_DATA = $(xml_in_files:.xml.in=.xml)
dbus_idle_built_sources = meta-dbus-idle-monitor.c meta-dbus-idle-monitor.h
gsettings_SCHEMAS = org.gnome.mutter.gschema.xml
@INTLTOOL_XML_NOMERGE_RULE@
@GSETTINGS_RULES@
@ -276,7 +314,7 @@ convertdir = $(datadir)/GConf/gsettings
convert_DATA = mutter-schemas.convert
CLEANFILES = \
mutter.desktop \
mutter-wayland.desktop \
mutter-wm.desktop \
org.gnome.mutter.gschema.xml \
$(xml_DATA) \
@ -286,7 +324,7 @@ CLEANFILES = \
pkgconfigdir = $(libdir)/pkgconfig
pkgconfig_DATA = libmutter.pc mutter-plugins.pc
pkgconfig_DATA = libmutter-wayland.pc
EXTRA_DIST=$(desktopfiles_files) \
$(wmproperties_files) \
@ -296,8 +334,7 @@ EXTRA_DIST=$(desktopfiles_files) \
$(xml_in_files) \
org.gnome.mutter.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
@ -323,3 +360,32 @@ mutter-enum-types.c: stamp-mutter-enum-types.h mutter-enum-types.c.in
$(libmutterinclude_base_headers) ) >> xgen-tetc && \
cp xgen-tetc mutter-enum-types.c && \
rm -f xgen-tetc
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 \
--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 < $< > $@

View File

@ -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,

View File

@ -84,6 +84,7 @@
#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 <X11/extensions/shape.h>
#include <X11/extensions/Xcomposite.h>
@ -172,7 +173,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 +328,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,40 +389,44 @@ 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)
{
@ -424,10 +437,19 @@ meta_begin_modal_for_plugin (MetaScreen *screen,
MetaDisplay *display = meta_screen_get_display (screen);
Display *xdpy = meta_display_get_xdisplay (display);
MetaCompositor *compositor = display->compositor;
ClutterStage *stage;
Window grab_window;
Cursor cursor = None;
gboolean pointer_grabbed = FALSE;
gboolean keyboard_grabbed = FALSE;
int result;
stage = CLUTTER_STAGE (meta_get_stage_for_screen (screen));
if (!stage)
return FALSE;
grab_window = clutter_x11_get_stage_window (stage);
if (compositor->modal_plugin != NULL || display->grab_op != META_GRAB_OP_NONE)
return FALSE;
@ -562,6 +584,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
@ -604,6 +631,7 @@ meta_compositor_manage_screen (MetaCompositor *compositor,
Display *xdisplay = meta_display_get_xdisplay (display);
Window xwin;
gint width, height;
MetaWaylandCompositor *wayland_compositor;
/* Check if the screen is already managed */
if (meta_screen_get_compositor_data (screen))
@ -616,7 +644,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 +662,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 +720,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 +728,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 +859,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 +1057,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 +1077,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
@ -1356,20 +1421,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 +1516,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)

View File

@ -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);

View File

@ -85,12 +85,20 @@ meta_plugin_manager_load (const gchar *plugin_name)
g_free (path);
}
static void
on_confirm_display_change (MetaMonitorManager *monitors,
MetaPluginManager *plugin_mgr)
{
meta_plugin_manager_confirm_display_change (plugin_mgr);
}
MetaPluginManager *
meta_plugin_manager_new (MetaScreen *screen)
{
MetaPluginManager *plugin_mgr;
MetaPluginClass *klass;
MetaPlugin *plugin;
MetaMonitorManager *monitors;
plugin_mgr = g_new0 (MetaPluginManager, 1);
plugin_mgr->screen = screen;
@ -101,6 +109,10 @@ meta_plugin_manager_new (MetaScreen *screen)
if (klass->start)
klass->start (plugin);
monitors = meta_monitor_manager_get ();
g_signal_connect (monitors, "confirm-display-change",
G_CALLBACK (on_confirm_display_change), plugin_mgr);
return plugin_mgr;
}
@ -317,6 +329,28 @@ 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
meta_plugin_manager_confirm_display_change (MetaPluginManager *plugin_mgr)
{
MetaPlugin *plugin = plugin_mgr->plugin;
MetaPluginClass *klass = META_PLUGIN_GET_CLASS (plugin);
if (klass->confirm_display_change)
return klass->confirm_display_change (plugin);
else
return meta_plugin_complete_display_change (plugin, TRUE);
}

View File

@ -73,4 +73,6 @@ gboolean meta_plugin_manager_filter_keybinding (MetaPluginManager *mgr,
gboolean meta_plugin_manager_xevent_filter (MetaPluginManager *mgr,
XEvent *xev);
void meta_plugin_manager_confirm_display_change (MetaPluginManager *mgr);
#endif

View File

@ -41,6 +41,7 @@
#include "compositor-private.h"
#include "meta-window-actor-private.h"
#include "monitor-private.h"
G_DEFINE_ABSTRACT_TYPE (MetaPlugin, meta_plugin, G_TYPE_OBJECT);
@ -266,10 +267,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
*
@ -290,15 +287,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);
}
/**
@ -338,3 +333,13 @@ meta_plugin_get_screen (MetaPlugin *plugin)
return priv->screen;
}
void
meta_plugin_complete_display_change (MetaPlugin *plugin,
gboolean ok)
{
MetaMonitorManager *manager;
manager = meta_monitor_manager_get ();
meta_monitor_manager_confirm_configuration (manager, ok);
}

View 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

View File

@ -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,67 @@ meta_shaped_texture_paint (ClutterActor *actor)
if (tex_width == 0 || tex_height == 0) /* no contents yet */
return;
ctx = clutter_backend_get_cogl_context (clutter_get_default_backend ());
fb = cogl_get_draw_framebuffer ();
opacity = clutter_actor_get_paint_opacity (actor);
clutter_actor_get_allocation_box (actor, &alloc);
if (priv->opaque_region != NULL && opacity == 255)
{
CoglPipeline *opaque_pipeline;
cairo_region_t *region;
int n_rects;
int i;
region = cairo_region_copy (priv->clip_region);
cairo_region_intersect (region, priv->opaque_region);
if (cairo_region_is_empty (region))
goto paint_blended;
opaque_pipeline = get_unblended_pipeline (ctx);
cogl_pipeline_set_layer_texture (opaque_pipeline, 0, paint_tex);
n_rects = cairo_region_num_rectangles (region);
for (i = 0; i < n_rects; i++)
{
cairo_rectangle_int_t rect;
cairo_region_get_rectangle (region, i, &rect);
paint_clipped_rectangle (fb, opaque_pipeline, &rect, &alloc);
}
cogl_object_unref (opaque_pipeline);
if (priv->clip_region != NULL)
{
blended_region = cairo_region_copy (priv->clip_region);
}
else
{
cairo_rectangle_int_t rect = { 0, 0, tex_width, tex_height };
blended_region = cairo_region_create_rectangle (&rect);
}
cairo_region_subtract (blended_region, priv->opaque_region);
paint_blended:
cairo_region_destroy (region);
}
if (blended_region == NULL && priv->clip_region != NULL)
blended_region = cairo_region_reference (priv->clip_region);
if (blended_region != NULL && cairo_region_is_empty (blended_region))
goto out;
if (priv->mask_texture == NULL)
{
/* Use a single-layer texture if we don't have a mask. */
if (priv->pipeline_unshaped == NULL)
{
if (G_UNLIKELY (pipeline_unshaped_template == NULL))
{
CoglContext *ctx = clutter_backend_get_cogl_context (clutter_get_default_backend ());
pipeline_unshaped_template = cogl_pipeline_new (ctx);
}
priv->pipeline_unshaped = cogl_pipeline_copy (pipeline_unshaped_template);
}
pipeline = priv->pipeline_unshaped;
pipeline = get_unmasked_pipeline (ctx);
}
else
{
if (priv->pipeline == NULL)
{
if (G_UNLIKELY (pipeline_template == NULL))
{
CoglContext *ctx = clutter_backend_get_cogl_context (clutter_get_default_backend ());
pipeline_template = cogl_pipeline_new (ctx);
cogl_pipeline_set_layer_combine (pipeline_template, 1,
"RGBA = MODULATE (PREVIOUS, TEXTURE[A])",
NULL);
}
priv->pipeline = cogl_pipeline_copy (pipeline_template);
}
pipeline = priv->pipeline;
pipeline = get_masked_pipeline (ctx);
cogl_pipeline_set_layer_texture (pipeline, 1, priv->mask_texture);
}
@ -216,20 +383,13 @@ meta_shaped_texture_paint (ClutterActor *actor)
{
CoglColor color;
guchar opacity = clutter_actor_get_paint_opacity (actor);
cogl_color_set_from_4ub (&color, opacity, opacity, opacity, opacity);
cogl_color_init_from_4ub (&color, opacity, opacity, opacity, opacity);
cogl_pipeline_set_color (pipeline, &color);
}
cogl_set_source (pipeline);
clutter_actor_get_allocation_box (actor, &alloc);
if (priv->clip_region)
if (blended_region != NULL)
{
int n_rects;
int i;
cairo_rectangle_int_t tex_rect = { 0, 0, tex_width, tex_height };
/* Limit to how many separate rectangles we'll draw; beyond this just
* fall back and draw the whole thing */
@ -238,8 +398,8 @@ meta_shaped_texture_paint (ClutterActor *actor)
n_rects = cairo_region_num_rectangles (priv->clip_region);
if (n_rects <= MAX_RECTS)
{
float coords[8];
float x1, y1, x2, y2;
int i;
cairo_rectangle_int_t tex_rect = { 0, 0, tex_width, tex_height };
for (i = 0; i < n_rects; i++)
{
@ -250,32 +410,23 @@ meta_shaped_texture_paint (ClutterActor *actor)
if (!gdk_rectangle_intersect (&tex_rect, &rect, &rect))
continue;
x1 = rect.x;
y1 = rect.y;
x2 = rect.x + rect.width;
y2 = rect.y + rect.height;
coords[0] = rect.x / (alloc.x2 - alloc.x1);
coords[1] = rect.y / (alloc.y2 - alloc.y1);
coords[2] = (rect.x + rect.width) / (alloc.x2 - alloc.x1);
coords[3] = (rect.y + rect.height) / (alloc.y2 - alloc.y1);
coords[4] = coords[0];
coords[5] = coords[1];
coords[6] = coords[2];
coords[7] = coords[3];
cogl_rectangle_with_multitexture_coords (x1, y1, x2, y2,
&coords[0], 8);
paint_clipped_rectangle (fb, pipeline, &rect, &alloc);
}
return;
goto out;
}
}
cogl_rectangle (0, 0,
alloc.x2 - alloc.x1,
alloc.y2 - alloc.y1);
cogl_framebuffer_draw_rectangle (fb, pipeline,
0, 0,
alloc.x2 - alloc.x1,
alloc.y2 - alloc.y1);
out:
if (pipeline != NULL)
cogl_object_unref (pipeline);
if (blended_region != NULL)
cairo_region_destroy (blended_region);
}
static void
@ -285,38 +436,62 @@ 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;
int n_rects;
float *rectangles;
int i;
ClutterActorBox alloc;
guint tex_width, tex_height;
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 +541,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 +598,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 +624,142 @@ meta_shaped_texture_set_mask_texture (MetaShapedTexture *stex,
clutter_actor_queue_redraw (CLUTTER_ACTOR (stex));
}
void
meta_shaped_texture_update_area (MetaShapedTexture *stex,
int x,
int y,
int width,
int height)
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
queue_damage_redraw_with_clip (MetaShapedTexture *stex,
int x,
int y,
int width,
int height)
{
ClutterActor *self = CLUTTER_ACTOR (stex);
MetaShapedTexturePrivate *priv;
ClutterActorBox allocation;
float scale_x;
float scale_y;
cairo_rectangle_int_t clip;
/* NB: clutter_actor_queue_redraw_with_clip expects a box in the actor's
* coordinate space so we need to convert from surface coordinates to
* 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;
clutter_actor_queue_redraw_with_clip (self, &clip);
}
void
meta_shaped_texture_update_area (MetaShapedTexture *stex,
int x,
int y,
int width,
int height)
{
MetaShapedTexturePrivate *priv;
const cairo_rectangle_int_t clip = { x, y, width, height };
priv = stex->priv;
if (priv->texture == NULL)
return;
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);
}
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)
{
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 inavlid texture */
priv->tex_width = 0;
priv->tex_height = 0;
clutter_actor_queue_relayout (CLUTTER_ACTOR (stex));
}
clutter_actor_queue_redraw (CLUTTER_ACTOR (stex));
queue_damage_redraw_with_clip (stex, x, y, width, height);
}
/**
@ -501,16 +777,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,6 +857,41 @@ 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
@ -569,6 +928,36 @@ meta_shaped_texture_set_clip_region (MetaShapedTexture *stex,
priv->clip_region = NULL;
}
/**
* meta_shaped_texture_set_opaque_region:
* @stex: a #MetaShapedTexture
* @opaque_region: (transfer full): the region of the texture that
* can have blending turned off.
*
* As most windows have a large portion that does not require blending,
* we can easily turn off blending if we know the areas that do not
* require blending. This sets the region where we will not blend for
* optimization purposes.
*/
void
meta_shaped_texture_set_opaque_region (MetaShapedTexture *stex,
cairo_region_t *opaque_region)
{
MetaShapedTexturePrivate *priv;
g_return_if_fail (META_IS_SHAPED_TEXTURE (stex));
priv = stex->priv;
if (priv->opaque_region)
cairo_region_destroy (priv->opaque_region);
if (opaque_region)
priv->opaque_region = cairo_region_reference (opaque_region);
else
priv->opaque_region = NULL;
}
/**
* meta_shaped_texture_get_image:
* @stex: A #MetaShapedTexture

View File

@ -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);

View File

@ -10,7 +10,6 @@
#include <math.h>
#include <X11/extensions/shape.h>
#include <X11/extensions/Xcomposite.h>
#include <X11/extensions/Xdamage.h>
#include <X11/extensions/Xrender.h>
@ -28,10 +27,12 @@
#include "xprops.h"
#include "compositor-private.h"
#include "meta-shaped-texture-private.h"
#include "meta-shadow-factory-private.h"
#include "meta-window-actor-private.h"
#include "meta-texture-rectangle.h"
#include "region-utils.h"
#include "meta-wayland-private.h"
enum {
POSITION_CHANGED,
@ -64,14 +65,12 @@ struct _MetaWindowActorPrivate
MetaShadow *focused_shadow;
MetaShadow *unfocused_shadow;
Pixmap back_pixmap;
Damage damage;
guint8 opacity;
/* A region that matches the shape of the window, including frame bounds */
cairo_region_t *shape_region;
/* If the window has an input shape, a region that matches the shape */
cairo_region_t *input_region;
/* The opaque region, from _NET_WM_OPAQUE_REGION, intersected with
* the shape region. */
cairo_region_t *opaque_region;
@ -102,31 +101,41 @@ struct _MetaWindowActorPrivate
/* List of FrameData for recent frames */
GList *frames;
Pixmap back_pixmap; /* Not used in wayland compositor mode */
Damage damage; /* Not used in wayland compositor mode */
guint visible : 1;
guint mapped : 1;
guint argb32 : 1;
guint disposed : 1;
guint redecorating : 1;
guint needs_damage_all : 1;
guint received_damage : 1;
guint repaint_scheduled : 1;
/* If set, the client needs to be sent a _NET_WM_FRAME_DRAWN
* client message using the most recent frame in ->frames */
guint needs_frame_drawn : 1;
guint repaint_scheduled : 1;
guint needs_pixmap : 1;
guint needs_reshape : 1;
guint recompute_focused_shadow : 1;
guint recompute_unfocused_shadow : 1;
guint size_changed : 1;
guint updates_frozen : 1;
guint needs_destroy : 1;
guint no_shadow : 1;
/*
* None of these are used in wayland compositor mode...
*/
guint needs_damage_all : 1;
guint received_x11_damage : 1;
guint needs_pixmap : 1;
guint x11_size_changed : 1;
guint updates_frozen : 1;
guint unredirected : 1;
/* This is used to detect fullscreen windows that need to be unredirected */
@ -170,7 +179,7 @@ static gboolean meta_window_actor_get_paint_volume (ClutterActor *actor,
ClutterPaintVolume *volume);
static void meta_window_actor_detach (MetaWindowActor *self);
static void meta_window_actor_detach_x11_pixmap (MetaWindowActor *self);
static gboolean meta_window_actor_has_shadow (MetaWindowActor *self);
static void meta_window_actor_handle_updates (MetaWindowActor *self);
@ -304,18 +313,21 @@ window_decorated_notify (MetaWindow *mw,
else
new_xwindow = meta_window_get_xwindow (mw);
meta_window_actor_detach (self);
/*
* First of all, clean up any resources we are currently using and will
* be replacing.
*/
if (priv->damage != None)
if (!meta_is_wayland_compositor ())
{
meta_error_trap_push (display);
XDamageDestroy (xdisplay, priv->damage);
meta_error_trap_pop (display);
priv->damage = None;
meta_window_actor_detach_x11_pixmap (self);
/*
* First of all, clean up any resources we are currently using and will
* be replacing.
*/
if (priv->damage != None)
{
meta_error_trap_push (display);
XDamageDestroy (xdisplay, priv->damage);
meta_error_trap_pop (display);
priv->damage = None;
}
}
priv->xwindow = new_xwindow;
@ -346,8 +358,9 @@ meta_window_actor_constructed (GObject *object)
Display *xdisplay = meta_display_get_xdisplay (display);
XRenderPictFormat *format;
priv->damage = XDamageCreate (xdisplay, xwindow,
XDamageReportBoundingBox);
if (!meta_is_wayland_compositor ())
priv->damage = XDamageCreate (xdisplay, xwindow,
XDamageReportBoundingBox);
format = XRenderFindVisualFormat (xdisplay, window->xvisual);
@ -356,10 +369,16 @@ meta_window_actor_constructed (GObject *object)
if (!priv->actor)
{
priv->actor = meta_shaped_texture_new ();
if (meta_is_wayland_compositor ())
priv->actor = meta_shaped_texture_new_with_wayland_surface (window->surface);
else
priv->actor = meta_shaped_texture_new_with_xwindow (xwindow);
clutter_actor_add_child (CLUTTER_ACTOR (self), priv->actor);
if (meta_is_wayland_compositor ())
clutter_actor_set_reactive (priv->actor, TRUE);
/*
* Since we are holding a pointer to this actor independently of the
* ClutterContainer internals, and provide a public API to access it,
@ -369,10 +388,10 @@ meta_window_actor_constructed (GObject *object)
*/
g_object_ref (priv->actor);
g_signal_connect (window, "notify::decorated",
G_CALLBACK (window_decorated_notify), self);
g_signal_connect (window, "notify::appears-focused",
G_CALLBACK (window_appears_focused_notify), self);
g_signal_connect_object (window, "notify::decorated",
G_CALLBACK (window_decorated_notify), self, 0);
g_signal_connect_object (window, "notify::appears-focused",
G_CALLBACK (window_appears_focused_notify), self, 0);
}
else
{
@ -385,9 +404,10 @@ meta_window_actor_constructed (GObject *object)
meta_window_actor_update_opacity (self);
/* Start off with an empty region to maintain the invariant that
the shape region is always set */
priv->shape_region = cairo_region_create();
/* Start off with empty regions to maintain the invariant that
these regions are always set */
priv->shape_region = cairo_region_create ();
priv->input_region = cairo_region_create ();
}
static void
@ -406,13 +426,18 @@ meta_window_actor_dispose (GObject *object)
priv->disposed = TRUE;
screen = priv->screen;
display = meta_screen_get_display (screen);
xdisplay = meta_display_get_xdisplay (display);
info = meta_screen_get_compositor_data (screen);
meta_window_actor_detach (self);
if (!meta_is_wayland_compositor ())
{
display = meta_screen_get_display (screen);
xdisplay = meta_display_get_xdisplay (display);
meta_window_actor_detach_x11_pixmap (self);
}
g_clear_pointer (&priv->shape_region, cairo_region_destroy);
g_clear_pointer (&priv->input_region, cairo_region_destroy);
g_clear_pointer (&priv->opaque_region, cairo_region_destroy);
g_clear_pointer (&priv->shadow_clip, cairo_region_destroy);
@ -421,7 +446,7 @@ meta_window_actor_dispose (GObject *object)
g_clear_pointer (&priv->unfocused_shadow, meta_shadow_unref);
g_clear_pointer (&priv->shadow_shape, meta_window_shape_unref);
if (priv->damage != None)
if (!meta_is_wayland_compositor () && priv->damage != None)
{
meta_error_trap_push (display);
XDamageDestroy (xdisplay, priv->damage);
@ -893,7 +918,8 @@ meta_window_actor_showing_on_its_workspace (MetaWindowActor *self)
static void
meta_window_actor_freeze (MetaWindowActor *self)
{
self->priv->freeze_count++;
if (!meta_is_wayland_compositor ())
self->priv->freeze_count++;
}
static void
@ -922,30 +948,33 @@ meta_window_actor_damage_all (MetaWindowActor *self)
static void
meta_window_actor_thaw (MetaWindowActor *self)
{
self->priv->freeze_count--;
if (G_UNLIKELY (self->priv->freeze_count < 0))
if (!meta_is_wayland_compositor ())
{
g_warning ("Error in freeze/thaw accounting.");
self->priv->freeze_count = 0;
return;
self->priv->freeze_count--;
if (G_UNLIKELY (self->priv->freeze_count < 0))
{
g_warning ("Error in freeze/thaw accounting.");
self->priv->freeze_count = 0;
return;
}
if (self->priv->freeze_count)
return;
/* We sometimes ignore moves and resizes on frozen windows */
meta_window_actor_sync_actor_geometry (self, FALSE);
/* We do this now since we might be going right back into the
* frozen state */
meta_window_actor_handle_updates (self);
/* Since we ignore damage events while a window is frozen for certain effects
* we may need to issue an update_area() covering the whole pixmap if we
* don't know what real damage has happened. */
if (self->priv->needs_damage_all)
meta_window_actor_damage_all (self);
}
if (self->priv->freeze_count)
return;
/* We sometimes ignore moves and resizes on frozen windows */
meta_window_actor_sync_actor_geometry (self, FALSE);
/* We do this now since we might be going right back into the
* frozen state */
meta_window_actor_handle_updates (self);
/* Since we ignore damage events while a window is frozen for certain effects
* we may need to issue an update_area() covering the whole pixmap if we
* don't know what real damage has happened. */
if (self->priv->needs_damage_all)
meta_window_actor_damage_all (self);
}
void
@ -976,7 +1005,7 @@ meta_window_actor_queue_frame_drawn (MetaWindowActor *self,
* send a _NET_WM_FRAME_DRAWN. We do a 1-pixel redraw to get
* consistent timing with non-empty frames.
*/
if (priv->mapped && !priv->needs_pixmap)
if (priv->mapped && (!meta_is_wayland_compositor () || !priv->needs_pixmap))
{
const cairo_rectangle_int_t clip = { 0, 0, 1, 1 };
clutter_actor_queue_redraw_with_clip (priv->actor, &clip);
@ -1002,7 +1031,7 @@ is_frozen (MetaWindowActor *self)
}
static void
meta_window_actor_queue_create_pixmap (MetaWindowActor *self)
meta_window_actor_queue_create_x11_pixmap (MetaWindowActor *self)
{
MetaWindowActorPrivate *priv = self->priv;
@ -1106,11 +1135,14 @@ meta_window_actor_after_effects (MetaWindowActor *self)
meta_window_actor_sync_visibility (self);
meta_window_actor_sync_actor_geometry (self, FALSE);
if (!meta_window_is_mapped (priv->window))
meta_window_actor_detach (self);
if (!meta_is_wayland_compositor ())
{
if (!meta_window_is_mapped (priv->window))
meta_window_actor_detach_x11_pixmap (self);
if (priv->needs_pixmap)
clutter_actor_queue_redraw (priv->actor);
if (priv->needs_pixmap)
clutter_actor_queue_redraw (priv->actor);
}
}
void
@ -1191,7 +1223,7 @@ meta_window_actor_effect_completed (MetaWindowActor *self,
* pixmap for a new size.
*/
static void
meta_window_actor_detach (MetaWindowActor *self)
meta_window_actor_detach_x11_pixmap (MetaWindowActor *self)
{
MetaWindowActorPrivate *priv = self->priv;
MetaScreen *screen = priv->screen;
@ -1212,7 +1244,7 @@ meta_window_actor_detach (MetaWindowActor *self)
XFreePixmap (xdisplay, priv->back_pixmap);
priv->back_pixmap = None;
meta_window_actor_queue_create_pixmap (self);
meta_window_actor_queue_create_x11_pixmap (self);
}
gboolean
@ -1227,7 +1259,7 @@ meta_window_actor_should_unredirect (MetaWindowActor *self)
if (priv->opacity != 0xff)
return FALSE;
if (metaWindow->has_shape)
if (metaWindow->shape_region != NULL)
return FALSE;
if (priv->argb32 && !meta_window_requested_bypass_compositor (metaWindow))
@ -1242,7 +1274,7 @@ meta_window_actor_should_unredirect (MetaWindowActor *self)
if (meta_window_is_override_redirect (metaWindow))
return TRUE;
if (priv->does_full_damage)
if (!meta_is_wayland_compositor () && priv->does_full_damage)
return TRUE;
return FALSE;
@ -1262,7 +1294,7 @@ meta_window_actor_set_redirected (MetaWindowActor *self, gboolean state)
meta_error_trap_push (display);
XCompositeRedirectWindow (xdisplay, xwin, CompositeRedirectManual);
meta_error_trap_pop (display);
meta_window_actor_detach (self);
meta_window_actor_detach_x11_pixmap (self);
self->priv->unredirected = FALSE;
}
else
@ -1284,6 +1316,9 @@ meta_window_actor_destroy (MetaWindowActor *self)
priv = self->priv;
if (meta_is_wayland_compositor ())
meta_shaped_texture_set_wayland_surface (META_SHAPED_TEXTURE (priv->actor), NULL);
window = priv->window;
window_type = meta_window_get_window_type (window);
meta_window_set_compositor_private (window, NULL);
@ -1335,15 +1370,21 @@ meta_window_actor_sync_actor_geometry (MetaWindowActor *self,
meta_window_get_input_rect (priv->window, &window_rect);
if (priv->last_width != window_rect.width ||
priv->last_height != window_rect.height)
/* When running as a display server then we instead catch size changes when
* new buffers are attached */
if (!meta_is_wayland_compositor ())
{
priv->size_changed = TRUE;
meta_window_actor_queue_create_pixmap (self);
meta_window_actor_update_shape (self);
if (priv->last_width != window_rect.width ||
priv->last_height != window_rect.height)
{
priv->x11_size_changed = TRUE;
meta_window_actor_queue_create_x11_pixmap (self);
priv->last_width = window_rect.width;
priv->last_height = window_rect.height;
meta_window_actor_update_shape (self);
priv->last_width = window_rect.width;
priv->last_height = window_rect.height;
}
}
if (meta_window_actor_effect_in_progress (self))
@ -1507,16 +1548,25 @@ meta_window_actor_new (MetaWindow *window)
MetaWindowActor *self;
MetaWindowActorPrivate *priv;
MetaFrame *frame;
Window top_window;
Window top_window = None;
ClutterActor *window_group;
frame = meta_window_get_frame (window);
if (frame)
top_window = meta_frame_get_xwindow (frame);
else
top_window = meta_window_get_xwindow (window);
if (window->client_type == META_WINDOW_CLIENT_TYPE_X11)
{
frame = meta_window_get_frame (window);
if (frame)
top_window = meta_frame_get_xwindow (frame);
else
top_window = meta_window_get_xwindow (window);
meta_verbose ("add window: Meta %p, xwin 0x%x\n", window, (guint)top_window);
meta_verbose ("add window: Meta %p, xwin 0x%x\n", window, (guint)top_window);
}
else
{
meta_verbose ("add window: Meta %p, wayland surface %p\n",
window, window->surface);
top_window = None;
}
self = g_object_new (META_TYPE_WINDOW_ACTOR,
"meta-window", window,
@ -1525,22 +1575,25 @@ meta_window_actor_new (MetaWindow *window)
NULL);
priv = self->priv;
priv->last_width = -1;
priv->last_height = -1;
priv->mapped = meta_window_toplevel_is_mapped (priv->window);
if (priv->mapped)
meta_window_actor_queue_create_pixmap (self);
meta_window_actor_set_updates_frozen (self,
meta_window_updates_are_frozen (priv->window));
if (!meta_is_wayland_compositor ())
{
priv->last_width = -1;
priv->last_height = -1;
/* If a window doesn't start off with updates frozen, we should
* we should send a _NET_WM_FRAME_DRAWN immediately after the first drawn.
*/
if (priv->window->extended_sync_request_counter && !priv->updates_frozen)
meta_window_actor_queue_frame_drawn (self, FALSE);
if (priv->mapped)
meta_window_actor_queue_create_x11_pixmap (self);
meta_window_actor_set_updates_frozen (self,
meta_window_updates_are_frozen (priv->window));
/* If a window doesn't start off with updates frozen, we should
* we should send a _NET_WM_FRAME_DRAWN immediately after the first drawn.
*/
if (priv->window->extended_sync_request_counter && !priv->updates_frozen)
meta_window_actor_queue_frame_drawn (self, FALSE);
}
meta_window_actor_sync_actor_geometry (self, priv->window->placed);
@ -1575,7 +1628,8 @@ meta_window_actor_mapped (MetaWindowActor *self)
priv->mapped = TRUE;
meta_window_actor_queue_create_pixmap (self);
if (!meta_is_wayland_compositor ())
meta_window_actor_queue_create_x11_pixmap (self);
}
void
@ -1590,8 +1644,11 @@ meta_window_actor_unmapped (MetaWindowActor *self)
if (meta_window_actor_effect_in_progress (self))
return;
meta_window_actor_detach (self);
priv->needs_pixmap = FALSE;
if (!meta_is_wayland_compositor ())
{
meta_window_actor_detach_x11_pixmap (self);
priv->needs_pixmap = FALSE;
}
}
/**
@ -1609,10 +1666,21 @@ meta_window_actor_get_obscured_region (MetaWindowActor *self)
{
MetaWindowActorPrivate *priv = self->priv;
if (priv->back_pixmap && priv->opacity == 0xff && !priv->window->shaded)
return priv->opaque_region;
else
return NULL;
if (!priv->window->shaded)
{
if (meta_is_wayland_compositor ())
{
if (priv->opacity == 0xff)
return priv->opaque_region;
}
else
{
if (priv->back_pixmap && priv->opacity == 0xff)
return priv->opaque_region;
}
}
return NULL;
}
#if 0
@ -1725,8 +1793,11 @@ meta_window_actor_reset_visible_regions (MetaWindowActor *self)
g_clear_pointer (&priv->shadow_clip, cairo_region_destroy);
}
/* When running as a wayland compositor we don't make requests for
* replacement pixmaps when resizing windows, we will instead be
* asked to attach replacement buffers by the clients. */
static void
check_needs_pixmap (MetaWindowActor *self)
check_needs_x11_pixmap (MetaWindowActor *self)
{
MetaWindowActorPrivate *priv = self->priv;
MetaScreen *screen = priv->screen;
@ -1748,10 +1819,10 @@ check_needs_pixmap (MetaWindowActor *self)
compositor = meta_display_get_compositor (display);
if (priv->size_changed)
if (priv->x11_size_changed)
{
meta_window_actor_detach (self);
priv->size_changed = FALSE;
meta_window_actor_detach_x11_pixmap (self);
priv->x11_size_changed = FALSE;
}
meta_error_trap_push (display);
@ -1883,13 +1954,13 @@ check_needs_shadow (MetaWindowActor *self)
}
void
meta_window_actor_process_damage (MetaWindowActor *self,
XDamageNotifyEvent *event)
meta_window_actor_process_x11_damage (MetaWindowActor *self,
XDamageNotifyEvent *event)
{
MetaWindowActorPrivate *priv = self->priv;
MetaCompScreen *info = meta_screen_get_compositor_data (priv->screen);
priv->received_damage = TRUE;
priv->received_x11_damage = TRUE;
if (meta_window_is_fullscreen (priv->window) && g_list_last (info->windows)->data == self && !priv->unredirected)
{
@ -1943,6 +2014,23 @@ meta_window_actor_process_damage (MetaWindowActor *self,
priv->repaint_scheduled = TRUE;
}
void
meta_window_actor_process_wayland_damage (MetaWindowActor *self,
int x,
int y,
int width,
int height)
{
MetaWindowActorPrivate *priv = self->priv;
if (!priv->mapped)
return;
meta_shaped_texture_update_area (META_SHAPED_TEXTURE (priv->actor),
x, y, width, height);
priv->repaint_scheduled = TRUE;
}
void
meta_window_actor_sync_visibility (MetaWindowActor *self)
{
@ -2083,21 +2171,35 @@ build_and_scan_frame_mask (MetaWindowActor *self,
}
static void
check_needs_reshape (MetaWindowActor *self)
meta_window_actor_update_shape_region (MetaWindowActor *self,
cairo_rectangle_int_t *client_area)
{
MetaWindowActorPrivate *priv = self->priv;
MetaScreen *screen = priv->screen;
MetaDisplay *display = meta_screen_get_display (screen);
MetaFrameBorders borders;
cairo_region_t *region = NULL;
cairo_rectangle_int_t client_area;
gboolean needs_mask;
if (!priv->mapped)
return;
if (priv->window->frame != NULL && priv->window->shape_region != NULL)
{
region = cairo_region_copy (priv->window->shape_region);
cairo_region_translate (region, client_area->x, client_area->y);
}
else if (priv->window->shape_region != NULL)
{
region = cairo_region_reference (priv->window->shape_region);
}
else
{
/* If we don't have a shape on the server, that means that
* we have an implicit shape of one rectangle covering the
* entire window. */
region = cairo_region_create_rectangle (client_area);
}
if (!priv->needs_reshape)
return;
meta_shaped_texture_set_mask_texture (META_SHAPED_TEXTURE (priv->actor), NULL);
if ((priv->window->shape_region != NULL) || (priv->window->frame != NULL))
build_and_scan_frame_mask (self, client_area, region);
g_clear_pointer (&priv->shape_region, cairo_region_destroy);
priv->shape_region = region;
if (priv->shadow_shape != NULL)
{
@ -2105,83 +2207,57 @@ check_needs_reshape (MetaWindowActor *self)
priv->shadow_shape = NULL;
}
meta_frame_calc_borders (priv->window->frame, &borders);
meta_window_actor_invalidate_shadow (self);
}
client_area.x = borders.total.left;
client_area.y = borders.total.top;
client_area.width = priv->window->rect.width;
if (priv->window->shaded)
client_area.height = 0;
else
client_area.height = priv->window->rect.height;
static void
meta_window_actor_update_input_region (MetaWindowActor *self,
cairo_rectangle_int_t *client_area)
{
MetaWindowActorPrivate *priv = self->priv;
MetaShapedTexture *stex = META_SHAPED_TEXTURE (priv->actor);
cairo_region_t *region = NULL;
meta_shaped_texture_set_mask_texture (META_SHAPED_TEXTURE (priv->actor), NULL);
g_clear_pointer (&priv->shape_region, cairo_region_destroy);
g_clear_pointer (&priv->opaque_region, cairo_region_destroy);
#ifdef HAVE_SHAPE
if (priv->window->has_shape)
if (priv->window->frame != NULL && priv->window->shape_region != NULL)
{
/* Translate the set of XShape rectangles that we
* get from the X server to a cairo_region. */
Display *xdisplay = meta_display_get_xdisplay (display);
XRectangle *rects;
int n_rects, ordering;
cairo_region_t *client_region = cairo_region_copy (priv->window->input_region);
meta_error_trap_push (display);
rects = XShapeGetRectangles (xdisplay,
priv->window->xwindow,
ShapeBounding,
&n_rects,
&ordering);
meta_error_trap_pop (display);
region = meta_frame_get_frame_bounds (priv->window->frame);
if (rects)
{
int i;
cairo_rectangle_int_t *cairo_rects = g_new (cairo_rectangle_int_t, n_rects);
for (i = 0; i < n_rects; i ++)
{
cairo_rects[i].x = rects[i].x + client_area.x;
cairo_rects[i].y = rects[i].y + client_area.y;
cairo_rects[i].width = rects[i].width;
cairo_rects[i].height = rects[i].height;
}
XFree (rects);
region = cairo_region_create_rectangles (cairo_rects, n_rects);
g_free (cairo_rects);
}
cairo_region_subtract_rectangle (region, client_area);
cairo_region_translate (client_region, client_area->x, client_area->y);
cairo_region_union (region, client_region);
cairo_region_destroy (client_region);
}
#endif
needs_mask = (region != NULL) || (priv->window->frame != NULL);
if (region != NULL)
else if (priv->window->shape_region != NULL)
{
/* The shape we get back from the client may have coordinates
* outside of the frame. The X SHAPE Extension requires that
* the overall shape the client provides never exceeds the
* "bounding rectangle" of the window -- the shape that the
* window would have gotten if it was unshaped. In our case,
* this is simply the client area.
*/
cairo_region_intersect_rectangle (region, &client_area);
region = cairo_region_reference (priv->window->input_region);
}
else
{
/* If we don't have a shape on the server, that means that
* we have an implicit shape of one rectangle covering the
* entire window. */
region = cairo_region_create_rectangle (&client_area);
region = cairo_region_create_rectangle (client_area);
}
/* The region at this point should be constrained to the
* bounds of the client rectangle. */
meta_shaped_texture_set_input_shape_region (stex, region);
cairo_region_destroy (region);
}
static void
meta_window_actor_update_opaque_region (MetaWindowActor *self)
{
MetaWindowActorPrivate *priv = self->priv;
g_clear_pointer (&priv->opaque_region, cairo_region_destroy);
if (priv->argb32 && priv->window->opaque_region != NULL)
{
MetaFrameBorders borders;
meta_frame_calc_borders (priv->window->frame, &borders);
/* The opaque region is defined to be a part of the
* window which ARGB32 will always paint with opaque
* pixels. For these regions, we want to avoid painting
@ -2193,27 +2269,46 @@ check_needs_reshape (MetaWindowActor *self)
* case, graphical glitches will occur.
*/
priv->opaque_region = cairo_region_copy (priv->window->opaque_region);
cairo_region_translate (priv->opaque_region, client_area.x, client_area.y);
cairo_region_intersect (priv->opaque_region, region);
cairo_region_translate (priv->opaque_region, borders.total.left, borders.total.top);
cairo_region_intersect (priv->opaque_region, priv->shape_region);
}
else if (priv->argb32)
priv->opaque_region = NULL;
else
priv->opaque_region = cairo_region_reference (region);
priv->opaque_region = cairo_region_reference (priv->shape_region);
if (needs_mask)
{
/* This takes the region, generates a mask using GTK+
* and scans the mask looking for all opaque pixels,
* adding it to region.
*/
build_and_scan_frame_mask (self, &client_area, region);
}
meta_shaped_texture_set_opaque_region (META_SHAPED_TEXTURE (priv->actor),
priv->opaque_region);
}
priv->shape_region = region;
static void
check_needs_reshape (MetaWindowActor *self)
{
MetaWindowActorPrivate *priv = self->priv;
MetaFrameBorders borders;
cairo_rectangle_int_t client_area;
if (!priv->mapped)
return;
if (!priv->needs_reshape)
return;
meta_frame_calc_borders (priv->window->frame, &borders);
client_area.x = borders.total.left;
client_area.y = borders.total.top;
client_area.width = priv->window->rect.width;
if (priv->window->shaded)
client_area.height = 0;
else
client_area.height = priv->window->rect.height;
meta_window_actor_update_shape_region (self, &client_area);
meta_window_actor_update_input_region (self, &client_area);
meta_window_actor_update_opaque_region (self);
priv->needs_reshape = FALSE;
meta_window_actor_invalidate_shadow (self);
}
void
@ -2229,6 +2324,69 @@ meta_window_actor_update_shape (MetaWindowActor *self)
clutter_actor_queue_redraw (priv->actor);
}
static void
maybe_emit_size_changed (MetaWindowActor *self,
MetaWaylandBuffer *new_buffer)
{
MetaWindowActorPrivate *priv = self->priv;
int width = 0, height = 0;
if (new_buffer)
{
width = new_buffer->width;
height = new_buffer->height;
}
if (priv->last_width != width || priv->last_height != height)
{
meta_window_actor_update_shape (self);
/* ::size-changed is supposed to refer to meta_window_get_outer_rect()
* but here we are only looking at buffer size changes.
*
* Emitting it here works pretty much OK because a new buffer size (which
* will correspond to the outer rect with the addition of invisible
* borders) also normally implies a change to the outer rect. In the rare
* case where a change to the window size was exactly balanced by a
* change to the invisible borders, we would miss emitting the signal.
*/
g_signal_emit (self, signals[SIZE_CHANGED], 0);
priv->last_width = width;
priv->last_height = height;
}
}
void
meta_window_actor_set_wayland_surface (MetaWindowActor *self,
MetaWaylandSurface *surface)
{
MetaWindowActorPrivate *priv = self->priv;
meta_shaped_texture_set_wayland_surface (META_SHAPED_TEXTURE (priv->actor),
surface);
if (surface && surface->buffer_ref.buffer)
maybe_emit_size_changed (self, surface->buffer_ref.buffer);
meta_window_actor_invalidate_shadow (self);
}
void
meta_window_actor_attach_wayland_buffer (MetaWindowActor *self,
MetaWaylandBuffer *buffer)
{
MetaWindowActorPrivate *priv = self->priv;
MetaShapedTexture *stex = META_SHAPED_TEXTURE (priv->actor);
CoglTexture *prev_tex = meta_shaped_texture_get_texture (stex);
meta_shaped_texture_attach_wayland_buffer (stex, buffer);
if (!prev_tex)
meta_window_actor_sync_actor_geometry (self, FALSE);
maybe_emit_size_changed (self, buffer);
}
static void
meta_window_actor_handle_updates (MetaWindowActor *self)
{
@ -2244,42 +2402,46 @@ meta_window_actor_handle_updates (MetaWindowActor *self)
return;
}
if (priv->unredirected)
if (!meta_is_wayland_compositor ())
{
/* Nothing to do here until/if the window gets redirected again */
return;
if (priv->unredirected)
{
/* Nothing to do here until/if the window gets redirected again */
return;
}
if (priv->received_x11_damage)
{
meta_error_trap_push (display);
XDamageSubtract (xdisplay, priv->damage, None, None);
meta_error_trap_pop (display);
/* We need to make sure that any X drawing that happens before the
* XDamageSubtract() above is visible to subsequent GL rendering;
* the only standardized way to do this is EXT_x11_sync_object,
* which isn't yet widely available. For now, we count on details
* of Xorg and the open source drivers, and hope for the best
* otherwise.
*
* Xorg and open source driver specifics:
*
* The X server makes sure to flush drawing to the kernel before
* sending out damage events, but since we use DamageReportBoundingBox
* there may be drawing between the last damage event and the
* XDamageSubtract() that needs to be flushed as well.
*
* Xorg always makes sure that drawing is flushed to the kernel
* before writing events or responses to the client, so any round trip
* request at this point is sufficient to flush the GLX buffers.
*/
XSync (xdisplay, False);
priv->received_x11_damage = FALSE;
}
check_needs_x11_pixmap (self);
}
if (priv->received_damage)
{
meta_error_trap_push (display);
XDamageSubtract (xdisplay, priv->damage, None, None);
meta_error_trap_pop (display);
/* We need to make sure that any X drawing that happens before the
* XDamageSubtract() above is visible to subsequent GL rendering;
* the only standardized way to do this is EXT_x11_sync_object,
* which isn't yet widely available. For now, we count on details
* of Xorg and the open source drivers, and hope for the best
* otherwise.
*
* Xorg and open source driver specifics:
*
* The X server makes sure to flush drawing to the kernel before
* sending out damage events, but since we use DamageReportBoundingBox
* there may be drawing between the last damage event and the
* XDamageSubtract() that needs to be flushed as well.
*
* Xorg always makes sure that drawing is flushed to the kernel
* before writing events or responses to the client, so any round trip
* request at this point is sufficient to flush the GLX buffers.
*/
XSync (xdisplay, False);
priv->received_damage = FALSE;
}
check_needs_pixmap (self);
check_needs_reshape (self);
check_needs_shadow (self);
}
@ -2458,16 +2620,20 @@ void
meta_window_actor_set_updates_frozen (MetaWindowActor *self,
gboolean updates_frozen)
{
MetaWindowActorPrivate *priv = self->priv;
updates_frozen = updates_frozen != FALSE;
if (priv->updates_frozen != updates_frozen)
/* On wayland we shouldn't need to ever freeze updates... */
if (!meta_is_wayland_compositor ())
{
priv->updates_frozen = updates_frozen;
if (updates_frozen)
meta_window_actor_freeze (self);
else
meta_window_actor_thaw (self);
MetaWindowActorPrivate *priv = self->priv;
updates_frozen = updates_frozen != FALSE;
if (priv->updates_frozen != updates_frozen)
{
priv->updates_frozen = updates_frozen;
if (updates_frozen)
meta_window_actor_freeze (self);
else
meta_window_actor_thaw (self);
}
}
}

View File

@ -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
{
@ -99,7 +100,7 @@ meta_window_group_paint (ClutterActor *actor)
int paint_x_offset, paint_y_offset;
MetaWindowGroup *window_group = META_WINDOW_GROUP (actor);
MetaCompScreen *info = meta_screen_get_compositor_data (window_group->screen);
MetaCompScreen *info;
/* 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
@ -136,13 +137,17 @@ meta_window_group_paint (ClutterActor *actor)
visible_region = cairo_region_create_rectangle (&visible_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 (visible_region, &unredirected_rect);
}
}
/* We walk the list from top to bottom (opposite of painting order),
@ -155,7 +160,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;

View File

@ -24,6 +24,8 @@
#include <meta/meta-plugin.h>
#include <meta/window.h>
#include <meta/util.h>
#include <meta/meta-background-group.h>
#include <meta/meta-background-actor.h>
#include <libintl.h>
#define _(x) dgettext (GETTEXT_PACKAGE, x)
@ -98,6 +100,8 @@ static void kill_window_effects (MetaPlugin *plugin,
MetaWindowActor *actor);
static void kill_switch_workspace (MetaPlugin *plugin);
static void confirm_display_change (MetaPlugin *plugin);
static const MetaPluginInfo * plugin_info (MetaPlugin *plugin);
META_PLUGIN_DECLARE(MetaDefaultPlugin, meta_default_plugin);
@ -113,6 +117,8 @@ struct _MetaDefaultPluginPrivate
ClutterActor *desktop1;
ClutterActor *desktop2;
ClutterActor *background_group;
MetaPluginInfo info;
};
@ -203,6 +209,7 @@ meta_default_plugin_class_init (MetaDefaultPluginClass *klass)
plugin_class->plugin_info = plugin_info;
plugin_class->kill_window_effects = kill_window_effects;
plugin_class->kill_switch_workspace = kill_switch_workspace;
plugin_class->confirm_display_change = confirm_display_change;
g_type_class_add_private (gobject_class, sizeof (MetaDefaultPluginPrivate));
}
@ -299,9 +306,58 @@ show_stage (MetaPlugin *plugin)
return FALSE;
}
static void
on_monitors_changed (MetaScreen *screen,
MetaPlugin *plugin)
{
MetaDefaultPlugin *self = META_DEFAULT_PLUGIN (plugin);
int i, n;
clutter_actor_destroy_all_children (self->priv->background_group);
n = meta_screen_get_n_monitors (screen);
for (i = 0; i < n; i++)
{
MetaRectangle rect;
ClutterActor *background;
ClutterColor color;
meta_screen_get_monitor_geometry (screen, i, &rect);
background = meta_background_actor_new ();
clutter_actor_set_position (background, rect.x, rect.y);
clutter_actor_set_size (background, rect.width, rect.height);
/* Don't use rand() here, mesa calls srand() internally when
parsing the driconf XML, but it's nice if the colors are
reproducible.
*/
clutter_color_init (&color,
g_random_int () % 255,
g_random_int () % 255,
g_random_int () % 255,
255);
clutter_actor_set_background_color (background, &color);
clutter_actor_add_child (self->priv->background_group, background);
}
}
static void
start (MetaPlugin *plugin)
{
MetaDefaultPlugin *self = META_DEFAULT_PLUGIN (plugin);
MetaScreen *screen = meta_plugin_get_screen (plugin);
self->priv->background_group = meta_background_group_new ();
clutter_actor_insert_child_below (meta_get_window_group_for_screen (screen),
self->priv->background_group, NULL);
g_signal_connect (screen, "monitors-changed",
G_CALLBACK (on_monitors_changed), plugin);
on_monitors_changed (screen, plugin);
meta_later_add (META_LATER_BEFORE_REDRAW,
(GSourceFunc) show_stage,
plugin,
@ -782,3 +838,33 @@ plugin_info (MetaPlugin *plugin)
return &priv->info;
}
static void
on_dialog_closed (GPid pid,
gint status,
gpointer user_data)
{
MetaPlugin *plugin = user_data;
gboolean ok;
ok = g_spawn_check_exit_status (status, NULL);
meta_plugin_complete_display_change (plugin, ok);
}
static void
confirm_display_change (MetaPlugin *plugin)
{
GPid pid;
pid = meta_show_dialog ("--question",
"Does the display look OK?",
"20",
NULL,
"_Keep This Configuration",
"_Restore Previous Configuration",
"preferences-desktop-display",
0,
NULL, NULL);
g_child_watch_add (pid, on_dialog_closed, plugin);
}

View File

@ -269,6 +269,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 +283,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);

View File

@ -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

View File

@ -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;
@ -1881,14 +1905,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 +1939,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 +1952,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 +1999,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, &timestamp))
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 +2037,7 @@ request_xserver_input_focus_change (MetaDisplay *display,
meta_display_ungrab (display);
update_focus_window (display,
type,
meta_window,
xwindow,
serial);
@ -2015,9 +2058,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 +2075,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 +2172,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 +2180,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,21 +2192,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);
@ -2157,6 +2214,14 @@ event_callback (XEvent *event,
#ifdef HAVE_STARTUP_NOTIFICATION
sn_display_process_event (display->sn_display, event);
#endif
/* Intercept XRandR events early and don't attempt any
processing for them. We still let them through to Gdk though,
so it can update its own internal state.
*/
monitor = meta_monitor_manager_get ();
if (meta_monitor_manager_handle_xevent (monitor, event))
return FALSE;
bypass_compositor = FALSE;
filter_out_event = FALSE;
@ -2170,11 +2235,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);
@ -2247,6 +2320,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 */
@ -2261,32 +2336,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
{
@ -2822,32 +2874,10 @@ event_callback (XEvent *event,
meta_stack_tracker_configure_event (screen->stack_tracker,
&event->xconfigure);
}
if (window && window->override_redirect)
meta_window_configure_notify (window, &event->xconfigure);
else
/* Handle screen resize */
{
MetaScreen *screen;
screen = meta_display_screen_for_root (display,
event->xconfigure.window);
if (screen != NULL)
{
#ifdef HAVE_RANDR
/* do the resize the official way */
XRRUpdateConfiguration (event);
#else
/* poke around in Xlib */
screen->xscreen->width = event->xconfigure.width;
screen->xscreen->height = event->xconfigure.height;
#endif
meta_screen_resize (screen,
event->xconfigure.width,
event->xconfigure.height);
}
}
break;
case ConfigureRequest:
/* This comment and code is found in both twm and fvwm */
@ -3138,6 +3168,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
@ -5789,6 +5843,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);
}
@ -5829,13 +5886,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);
}
@ -5847,6 +5907,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);
}

539
src/core/edid-parse.c Normal file
View File

@ -0,0 +1,539 @@
/*
* Copyright 2007 Red Hat, Inc.
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation
* on the rights to use, copy, modify, merge, publish, distribute, sub
* license, and/or sell copies of the Software, and to permit persons to whom
* the Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice (including the next
* paragraph) shall be included in all copies or substantial portions of the
* Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL
* THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
* IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
/* Author: Soren Sandmann <sandmann@redhat.com> */
#include "edid.h"
#include <stdlib.h>
#include <string.h>
#include <math.h>
#include <glib.h>
static int
get_bit (int in, int bit)
{
return (in & (1 << bit)) >> bit;
}
static int
get_bits (int in, int begin, int end)
{
int mask = (1 << (end - begin + 1)) - 1;
return (in >> begin) & mask;
}
static int
decode_header (const uchar *edid)
{
if (memcmp (edid, "\x00\xff\xff\xff\xff\xff\xff\x00", 8) == 0)
return TRUE;
return FALSE;
}
static int
decode_vendor_and_product_identification (const uchar *edid, MonitorInfo *info)
{
int is_model_year;
/* Manufacturer Code */
info->manufacturer_code[0] = get_bits (edid[0x08], 2, 6);
info->manufacturer_code[1] = get_bits (edid[0x08], 0, 1) << 3;
info->manufacturer_code[1] |= get_bits (edid[0x09], 5, 7);
info->manufacturer_code[2] = get_bits (edid[0x09], 0, 4);
info->manufacturer_code[3] = '\0';
info->manufacturer_code[0] += 'A' - 1;
info->manufacturer_code[1] += 'A' - 1;
info->manufacturer_code[2] += 'A' - 1;
/* Product Code */
info->product_code = edid[0x0b] << 8 | edid[0x0a];
/* Serial Number */
info->serial_number =
edid[0x0c] | edid[0x0d] << 8 | edid[0x0e] << 16 | edid[0x0f] << 24;
/* Week and Year */
is_model_year = FALSE;
switch (edid[0x10])
{
case 0x00:
info->production_week = -1;
break;
case 0xff:
info->production_week = -1;
is_model_year = TRUE;
break;
default:
info->production_week = edid[0x10];
break;
}
if (is_model_year)
{
info->production_year = -1;
info->model_year = 1990 + edid[0x11];
}
else
{
info->production_year = 1990 + edid[0x11];
info->model_year = -1;
}
return TRUE;
}
static int
decode_edid_version (const uchar *edid, MonitorInfo *info)
{
info->major_version = edid[0x12];
info->minor_version = edid[0x13];
return TRUE;
}
static int
decode_display_parameters (const uchar *edid, MonitorInfo *info)
{
/* Digital vs Analog */
info->is_digital = get_bit (edid[0x14], 7);
if (info->is_digital)
{
int bits;
static const int bit_depth[8] =
{
-1, 6, 8, 10, 12, 14, 16, -1
};
static const Interface interfaces[6] =
{
UNDEFINED, DVI, HDMI_A, HDMI_B, MDDI, DISPLAY_PORT
};
bits = get_bits (edid[0x14], 4, 6);
info->connector.digital.bits_per_primary = bit_depth[bits];
bits = get_bits (edid[0x14], 0, 3);
if (bits <= 5)
info->connector.digital.interface = interfaces[bits];
else
info->connector.digital.interface = UNDEFINED;
}
else
{
int bits = get_bits (edid[0x14], 5, 6);
static const double levels[][3] =
{
{ 0.7, 0.3, 1.0 },
{ 0.714, 0.286, 1.0 },
{ 1.0, 0.4, 1.4 },
{ 0.7, 0.0, 0.7 },
};
info->connector.analog.video_signal_level = levels[bits][0];
info->connector.analog.sync_signal_level = levels[bits][1];
info->connector.analog.total_signal_level = levels[bits][2];
info->connector.analog.blank_to_black = get_bit (edid[0x14], 4);
info->connector.analog.separate_hv_sync = get_bit (edid[0x14], 3);
info->connector.analog.composite_sync_on_h = get_bit (edid[0x14], 2);
info->connector.analog.composite_sync_on_green = get_bit (edid[0x14], 1);
info->connector.analog.serration_on_vsync = get_bit (edid[0x14], 0);
}
/* Screen Size / Aspect Ratio */
if (edid[0x15] == 0 && edid[0x16] == 0)
{
info->width_mm = -1;
info->height_mm = -1;
info->aspect_ratio = -1.0;
}
else if (edid[0x16] == 0)
{
info->width_mm = -1;
info->height_mm = -1;
info->aspect_ratio = 100.0 / (edid[0x15] + 99);
}
else if (edid[0x15] == 0)
{
info->width_mm = -1;
info->height_mm = -1;
info->aspect_ratio = 100.0 / (edid[0x16] + 99);
info->aspect_ratio = 1/info->aspect_ratio; /* portrait */
}
else
{
info->width_mm = 10 * edid[0x15];
info->height_mm = 10 * edid[0x16];
}
/* Gamma */
if (edid[0x17] == 0xFF)
info->gamma = -1.0;
else
info->gamma = (edid[0x17] + 100.0) / 100.0;
/* Features */
info->standby = get_bit (edid[0x18], 7);
info->suspend = get_bit (edid[0x18], 6);
info->active_off = get_bit (edid[0x18], 5);
if (info->is_digital)
{
info->connector.digital.rgb444 = TRUE;
if (get_bit (edid[0x18], 3))
info->connector.digital.ycrcb444 = 1;
if (get_bit (edid[0x18], 4))
info->connector.digital.ycrcb422 = 1;
}
else
{
int bits = get_bits (edid[0x18], 3, 4);
ColorType color_type[4] =
{
MONOCHROME, RGB, OTHER_COLOR, UNDEFINED_COLOR
};
info->connector.analog.color_type = color_type[bits];
}
info->srgb_is_standard = get_bit (edid[0x18], 2);
/* In 1.3 this is called "has preferred timing" */
info->preferred_timing_includes_native = get_bit (edid[0x18], 1);
/* FIXME: In 1.3 this indicates whether the monitor accepts GTF */
info->continuous_frequency = get_bit (edid[0x18], 0);
return TRUE;
}
static double
decode_fraction (int high, int low)
{
double result = 0.0;
int i;
high = (high << 2) | low;
for (i = 0; i < 10; ++i)
result += get_bit (high, i) * pow (2, i - 10);
return result;
}
static int
decode_color_characteristics (const uchar *edid, MonitorInfo *info)
{
info->red_x = decode_fraction (edid[0x1b], get_bits (edid[0x19], 6, 7));
info->red_y = decode_fraction (edid[0x1c], get_bits (edid[0x19], 5, 4));
info->green_x = decode_fraction (edid[0x1d], get_bits (edid[0x19], 2, 3));
info->green_y = decode_fraction (edid[0x1e], get_bits (edid[0x19], 0, 1));
info->blue_x = decode_fraction (edid[0x1f], get_bits (edid[0x1a], 6, 7));
info->blue_y = decode_fraction (edid[0x20], get_bits (edid[0x1a], 4, 5));
info->white_x = decode_fraction (edid[0x21], get_bits (edid[0x1a], 2, 3));
info->white_y = decode_fraction (edid[0x22], get_bits (edid[0x1a], 0, 1));
return TRUE;
}
static int
decode_established_timings (const uchar *edid, MonitorInfo *info)
{
static const Timing established[][8] =
{
{
{ 800, 600, 60 },
{ 800, 600, 56 },
{ 640, 480, 75 },
{ 640, 480, 72 },
{ 640, 480, 67 },
{ 640, 480, 60 },
{ 720, 400, 88 },
{ 720, 400, 70 }
},
{
{ 1280, 1024, 75 },
{ 1024, 768, 75 },
{ 1024, 768, 70 },
{ 1024, 768, 60 },
{ 1024, 768, 87 },
{ 832, 624, 75 },
{ 800, 600, 75 },
{ 800, 600, 72 }
},
{
{ 0, 0, 0 },
{ 0, 0, 0 },
{ 0, 0, 0 },
{ 0, 0, 0 },
{ 0, 0, 0 },
{ 0, 0, 0 },
{ 0, 0, 0 },
{ 1152, 870, 75 }
},
};
int i, j, idx;
idx = 0;
for (i = 0; i < 3; ++i)
{
for (j = 0; j < 8; ++j)
{
int byte = edid[0x23 + i];
if (get_bit (byte, j) && established[i][j].frequency != 0)
info->established[idx++] = established[i][j];
}
}
return TRUE;
}
static int
decode_standard_timings (const uchar *edid, MonitorInfo *info)
{
int i;
for (i = 0; i < 8; i++)
{
int first = edid[0x26 + 2 * i];
int second = edid[0x27 + 2 * i];
if (first != 0x01 && second != 0x01)
{
int w = 8 * (first + 31);
int h = 0;
switch (get_bits (second, 6, 7))
{
case 0x00: h = (w / 16) * 10; break;
case 0x01: h = (w / 4) * 3; break;
case 0x02: h = (w / 5) * 4; break;
case 0x03: h = (w / 16) * 9; break;
}
info->standard[i].width = w;
info->standard[i].height = h;
info->standard[i].frequency = get_bits (second, 0, 5) + 60;
}
}
return TRUE;
}
static void
decode_lf_string (const uchar *s, int n_chars, char *result)
{
int i;
for (i = 0; i < n_chars; ++i)
{
if (s[i] == 0x0a)
{
*result++ = '\0';
break;
}
else if (s[i] == 0x00)
{
/* Convert embedded 0's to spaces */
*result++ = ' ';
}
else
{
*result++ = s[i];
}
}
}
static void
decode_display_descriptor (const uchar *desc,
MonitorInfo *info)
{
switch (desc[0x03])
{
case 0xFC:
decode_lf_string (desc + 5, 13, info->dsc_product_name);
break;
case 0xFF:
decode_lf_string (desc + 5, 13, info->dsc_serial_number);
break;
case 0xFE:
decode_lf_string (desc + 5, 13, info->dsc_string);
break;
case 0xFD:
/* Range Limits */
break;
case 0xFB:
/* Color Point */
break;
case 0xFA:
/* Timing Identifications */
break;
case 0xF9:
/* Color Management */
break;
case 0xF8:
/* Timing Codes */
break;
case 0xF7:
/* Established Timings */
break;
case 0x10:
break;
}
}
static void
decode_detailed_timing (const uchar *timing,
DetailedTiming *detailed)
{
int bits;
StereoType stereo[] =
{
NO_STEREO, NO_STEREO, FIELD_RIGHT, FIELD_LEFT,
TWO_WAY_RIGHT_ON_EVEN, TWO_WAY_LEFT_ON_EVEN,
FOUR_WAY_INTERLEAVED, SIDE_BY_SIDE
};
detailed->pixel_clock = (timing[0x00] | timing[0x01] << 8) * 10000;
detailed->h_addr = timing[0x02] | ((timing[0x04] & 0xf0) << 4);
detailed->h_blank = timing[0x03] | ((timing[0x04] & 0x0f) << 8);
detailed->v_addr = timing[0x05] | ((timing[0x07] & 0xf0) << 4);
detailed->v_blank = timing[0x06] | ((timing[0x07] & 0x0f) << 8);
detailed->h_front_porch = timing[0x08] | get_bits (timing[0x0b], 6, 7) << 8;
detailed->h_sync = timing[0x09] | get_bits (timing[0x0b], 4, 5) << 8;
detailed->v_front_porch =
get_bits (timing[0x0a], 4, 7) | get_bits (timing[0x0b], 2, 3) << 4;
detailed->v_sync =
get_bits (timing[0x0a], 0, 3) | get_bits (timing[0x0b], 0, 1) << 4;
detailed->width_mm = timing[0x0c] | get_bits (timing[0x0e], 4, 7) << 8;
detailed->height_mm = timing[0x0d] | get_bits (timing[0x0e], 0, 3) << 8;
detailed->right_border = timing[0x0f];
detailed->top_border = timing[0x10];
detailed->interlaced = get_bit (timing[0x11], 7);
/* Stereo */
bits = get_bits (timing[0x11], 5, 6) << 1 | get_bit (timing[0x11], 0);
detailed->stereo = stereo[bits];
/* Sync */
bits = timing[0x11];
detailed->digital_sync = get_bit (bits, 4);
if (detailed->digital_sync)
{
detailed->connector.digital.composite = !get_bit (bits, 3);
if (detailed->connector.digital.composite)
{
detailed->connector.digital.serrations = get_bit (bits, 2);
detailed->connector.digital.negative_vsync = FALSE;
}
else
{
detailed->connector.digital.serrations = FALSE;
detailed->connector.digital.negative_vsync = !get_bit (bits, 2);
}
detailed->connector.digital.negative_hsync = !get_bit (bits, 0);
}
else
{
detailed->connector.analog.bipolar = get_bit (bits, 3);
detailed->connector.analog.serrations = get_bit (bits, 2);
detailed->connector.analog.sync_on_green = !get_bit (bits, 1);
}
}
static int
decode_descriptors (const uchar *edid, MonitorInfo *info)
{
int i;
int timing_idx;
timing_idx = 0;
for (i = 0; i < 4; ++i)
{
int index = 0x36 + i * 18;
if (edid[index + 0] == 0x00 && edid[index + 1] == 0x00)
{
decode_display_descriptor (edid + index, info);
}
else
{
decode_detailed_timing (edid + index, &(info->detailed_timings[timing_idx++]));
}
}
info->n_detailed_timings = timing_idx;
return TRUE;
}
static void
decode_check_sum (const uchar *edid,
MonitorInfo *info)
{
int i;
uchar check = 0;
for (i = 0; i < 128; ++i)
check += edid[i];
info->checksum = check;
}
MonitorInfo *
decode_edid (const uchar *edid)
{
MonitorInfo *info = g_new0 (MonitorInfo, 1);
decode_check_sum (edid, info);
if (decode_header (edid)
&& decode_vendor_and_product_identification (edid, info)
&& decode_edid_version (edid, info)
&& decode_display_parameters (edid, info)
&& decode_color_characteristics (edid, info)
&& decode_established_timings (edid, info)
&& decode_standard_timings (edid, info)
&& decode_descriptors (edid, info))
{
return info;
}
else
{
g_free (info);
return NULL;
}
}

195
src/core/edid.h Normal file
View File

@ -0,0 +1,195 @@
/* edid.h
*
* Copyright 2007, 2008, Red Hat, Inc.
*
* This file is part of the Gnome Library.
*
* The Gnome Library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public License as
* published by the Free Software Foundation; either version 2 of the
* License, or (at your option) any later version.
*
* The Gnome 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
* Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public
* License along with the Gnome Library; see the file COPYING.LIB. If not,
* write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
* Boston, MA 02110-1301, USA.
*
* Author: Soren Sandmann <sandmann@redhat.com>
*/
#ifndef EDID_H
#define EDID_H
typedef unsigned char uchar;
typedef struct MonitorInfo MonitorInfo;
typedef struct Timing Timing;
typedef struct DetailedTiming DetailedTiming;
typedef enum
{
UNDEFINED,
DVI,
HDMI_A,
HDMI_B,
MDDI,
DISPLAY_PORT
} Interface;
typedef enum
{
UNDEFINED_COLOR,
MONOCHROME,
RGB,
OTHER_COLOR
} ColorType;
typedef enum
{
NO_STEREO,
FIELD_RIGHT,
FIELD_LEFT,
TWO_WAY_RIGHT_ON_EVEN,
TWO_WAY_LEFT_ON_EVEN,
FOUR_WAY_INTERLEAVED,
SIDE_BY_SIDE
} StereoType;
struct Timing
{
int width;
int height;
int frequency;
};
struct DetailedTiming
{
int pixel_clock;
int h_addr;
int h_blank;
int h_sync;
int h_front_porch;
int v_addr;
int v_blank;
int v_sync;
int v_front_porch;
int width_mm;
int height_mm;
int right_border;
int top_border;
int interlaced;
StereoType stereo;
int digital_sync;
union
{
struct
{
int bipolar;
int serrations;
int sync_on_green;
} analog;
struct
{
int composite;
int serrations;
int negative_vsync;
int negative_hsync;
} digital;
} connector;
};
struct MonitorInfo
{
int checksum;
char manufacturer_code[4];
int product_code;
unsigned int serial_number;
int production_week; /* -1 if not specified */
int production_year; /* -1 if not specified */
int model_year; /* -1 if not specified */
int major_version;
int minor_version;
int is_digital;
union
{
struct
{
int bits_per_primary;
Interface interface;
int rgb444;
int ycrcb444;
int ycrcb422;
} digital;
struct
{
double video_signal_level;
double sync_signal_level;
double total_signal_level;
int blank_to_black;
int separate_hv_sync;
int composite_sync_on_h;
int composite_sync_on_green;
int serration_on_vsync;
ColorType color_type;
} analog;
} connector;
int width_mm; /* -1 if not specified */
int height_mm; /* -1 if not specified */
double aspect_ratio; /* -1.0 if not specififed */
double gamma; /* -1.0 if not specified */
int standby;
int suspend;
int active_off;
int srgb_is_standard;
int preferred_timing_includes_native;
int continuous_frequency;
double red_x;
double red_y;
double green_x;
double green_y;
double blue_x;
double blue_y;
double white_x;
double white_y;
Timing established[24]; /* Terminated by 0x0x0 */
Timing standard[8];
int n_detailed_timings;
DetailedTiming detailed_timings[4]; /* If monitor has a preferred
* mode, it is the first one
* (whether it has, is
* determined by the
* preferred_timing_includes
* bit.
*/
/* Optional product description */
char dsc_serial_number[14];
char dsc_product_name[14];
char dsc_string[14]; /* Unspecified ASCII data */
};
MonitorInfo *decode_edid (const uchar *data);
char *make_display_name (const MonitorInfo *info);
char *make_display_size_string (int width_mm, int height_mm);
#endif

View File

@ -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,

View File

@ -55,6 +55,7 @@
#include "session.h"
#include <meta/prefs.h>
#include <meta/compositor.h>
#include "meta-wayland-private.h"
#include <glib-object.h>
#include <gdk/gdkx.h>
@ -346,28 +347,64 @@ meta_finalize (void)
if (display)
meta_display_close (display,
CurrentTime); /* I doubt correct timestamps matter here */
if (meta_is_wayland_compositor ())
meta_wayland_finalize ();
}
static int sigterm_pipe_fds[2] = { -1, -1 };
static int signal_pipe_fds[2] = { -1, -1 };
static void
sigterm_handler (int signum)
signal_handler (int signum)
{
if (sigterm_pipe_fds[1] >= 0)
if (signal_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;
switch (signum)
{
case SIGTERM:
write (signal_pipe_fds[1], "T", 1);
break;
default:
break;
}
}
}
static gboolean
on_sigterm (void)
on_signal (GIOChannel *source,
GIOCondition condition,
void *data)
{
meta_quit (META_EXIT_SUCCESS);
return FALSE;
char signal;
int count;
for (;;)
{
count = read (signal_pipe_fds[0], &signal, 1);
if (count == EINTR)
continue;
if (count < 0)
{
const char *msg = strerror (errno);
g_warning ("Error handling signal: %s", msg);
}
if (count != 1)
{
g_warning ("Unexpectedly failed to read byte from signal pipe\n");
return TRUE;
}
break;
}
switch (signal)
{
case 'T': /* SIGTERM */
meta_quit (META_EXIT_SUCCESS);
break;
default:
g_warning ("Spurious character '%c' read from signal pipe", signal);
}
return TRUE;
}
/**
@ -396,17 +433,17 @@ meta_init (void)
g_strerror (errno));
#endif
if (pipe (sigterm_pipe_fds) != 0)
g_printerr ("Failed to create SIGTERM pipe: %s\n",
if (pipe (signal_pipe_fds) != 0)
g_printerr ("Failed to create signal pipe: %s\n",
g_strerror (errno));
channel = g_io_channel_unix_new (sigterm_pipe_fds[0]);
channel = g_io_channel_unix_new (signal_pipe_fds[0]);
g_io_channel_set_flags (channel, G_IO_FLAG_NONBLOCK, NULL);
g_io_add_watch (channel, G_IO_IN, (GIOFunc) on_sigterm, NULL);
g_io_add_watch (channel, G_IO_IN, (GIOFunc) on_signal, NULL);
g_io_channel_set_close_on_unref (channel, TRUE);
g_io_channel_unref (channel);
act.sa_handler = &sigterm_handler;
act.sa_handler = &signal_handler;
if (sigaction (SIGTERM, &act, NULL) < 0)
g_printerr ("Failed to register SIGTERM handler: %s\n",
g_strerror (errno));
@ -427,9 +464,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 +485,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 ();
}
}
/**

View 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

View 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);
}

View 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);

View 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);
}

View File

@ -0,0 +1,40 @@
/* -*- 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.
*/
/* This file is shared between mutter (src/core/meta-xrandr-shared.h)
and gnome-desktop (libgnome-desktop/meta-xrandr-shared.h).
The canonical place for all changes is mutter.
There should be no includes in this file.
*/
#ifndef META_XRANDR_SHARED_H
#define META_XRANDR_SHARED_H
typedef enum {
META_POWER_SAVE_UNKNOWN = -1,
META_POWER_SAVE_ON = 0,
META_POWER_SAVE_STANDBY,
META_POWER_SAVE_SUSPEND,
META_POWER_SAVE_OFF,
} MetaPowerSave;
#endif

1769
src/core/monitor-config.c Normal file

File diff suppressed because it is too large Load Diff

390
src/core/monitor-private.h Normal file
View File

@ -0,0 +1,390 @@
/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
/**
* \file screen-private.h Handling of monitor configuration
*
* Managing multiple monitors
* This file contains structures and functions that handle
* multiple monitors, including reading the current configuration
* and available hardware, and applying it.
*
* This interface is private to mutter, API users should look
* at MetaScreen instead.
*/
/*
* Copyright (C) 2001 Havoc Pennington
* Copyright (C) 2003 Rob Adams
* Copyright (C) 2004-2006 Elijah Newren
* 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_MONITOR_PRIVATE_H
#define META_MONITOR_PRIVATE_H
#include <cogl/cogl.h>
#include <libgnome-desktop/gnome-pnp-ids.h>
#include "display-private.h"
#include <meta/screen.h>
#include "stack-tracker.h"
#include "ui.h"
#ifdef HAVE_WAYLAND
#include <wayland-server.h>
#endif
#include "meta-xrandr-shared.h"
#include "meta-dbus-xrandr.h"
typedef struct _MetaMonitorManagerClass MetaMonitorManagerClass;
typedef struct _MetaMonitorManager MetaMonitorManager;
typedef struct _MetaMonitorConfigClass MetaMonitorConfigClass;
typedef struct _MetaMonitorConfig MetaMonitorConfig;
#ifndef HAVE_WAYLAND
enum wl_output_transform {
WL_OUTPUT_TRANSFORM_NORMAL,
WL_OUTPUT_TRANSFORM_90,
WL_OUTPUT_TRANSFORM_180,
WL_OUTPUT_TRANSFORM_270,
WL_OUTPUT_TRANSFORM_FLIPPED,
WL_OUTPUT_TRANSFORM_FLIPPED_90,
WL_OUTPUT_TRANSFORM_FLIPPED_180,
WL_OUTPUT_TRANSFORM_FLIPPED_270
};
#endif
typedef struct _MetaOutput MetaOutput;
typedef struct _MetaCRTC MetaCRTC;
typedef struct _MetaMonitorMode MetaMonitorMode;
typedef struct _MetaMonitorInfo MetaMonitorInfo;
typedef struct _MetaCRTCInfo MetaCRTCInfo;
typedef struct _MetaOutputInfo MetaOutputInfo;
struct _MetaOutput
{
/* The CRTC driving this output, NULL if the output is not enabled */
MetaCRTC *crtc;
/* The low-level ID of this output, used to apply back configuration */
glong output_id;
char *name;
char *vendor;
char *product;
char *serial;
int width_mm;
int height_mm;
CoglSubpixelOrder subpixel_order;
MetaMonitorMode *preferred_mode;
MetaMonitorMode **modes;
unsigned int n_modes;
MetaCRTC **possible_crtcs;
unsigned int n_possible_crtcs;
MetaOutput **possible_clones;
unsigned int n_possible_clones;
int backlight;
int backlight_min;
int backlight_max;
/* Used when changing configuration */
gboolean is_dirty;
/* The low-level bits used to build the high-level info
in MetaMonitorInfo
XXX: flags maybe?
There is a lot of code that uses MonitorInfo->is_primary,
but nobody uses MetaOutput yet
*/
gboolean is_primary;
gboolean is_presentation;
};
struct _MetaCRTC
{
glong crtc_id;
MetaRectangle rect;
MetaMonitorMode *current_mode;
enum wl_output_transform transform;
unsigned int all_transforms;
/* Only used to build the logical configuration
from the HW one
*/
MetaMonitorInfo *logical_monitor;
/* Used when changing configuration */
gboolean is_dirty;
};
struct _MetaMonitorMode
{
/* The low-level ID of this mode, used to apply back configuration */
glong mode_id;
int width;
int height;
float refresh_rate;
};
/**
* MetaMonitorInfo:
*
* A structure with high-level information about monitors.
* This corresponds to a subset of the compositor coordinate space.
* Clones are only reported once, irrespective of the way
* they're implemented (two CRTCs configured for the same
* coordinates or one CRTCs driving two outputs). Inactive CRTCs
* are ignored, and so are disabled outputs.
*/
struct _MetaMonitorInfo
{
int number;
int xinerama_index;
MetaRectangle rect;
gboolean is_primary;
gboolean is_presentation; /* XXX: not yet used */
gboolean in_fullscreen;
/* The primary or first output for this monitor, 0 if we can't figure out.
It can be matched to an output_id of a MetaOutput.
This is used as an opaque token on reconfiguration when switching from
clone to extened, to decide on what output the windows should go next
(it's an attempt to keep windows on the same monitor, and preferably on
the primary one).
*/
glong output_id;
};
/*
* MetaCRTCInfo:
* This represents the writable part of a CRTC, as deserialized from DBus
* or built by MetaMonitorConfig
*
* Note: differently from the other structures in this file, MetaCRTCInfo
* is handled by pointer. This is to accomodate the usage in MetaMonitorConfig
*/
struct _MetaCRTCInfo {
MetaCRTC *crtc;
MetaMonitorMode *mode;
int x;
int y;
enum wl_output_transform transform;
GPtrArray *outputs;
};
/*
* MetaOutputInfo:
* this is the same as MetaOutputInfo, but for CRTCs
*/
struct _MetaOutputInfo {
MetaOutput *output;
gboolean is_primary;
gboolean is_presentation;
};
#define META_TYPE_MONITOR_MANAGER (meta_monitor_manager_get_type ())
#define META_MONITOR_MANAGER(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), META_TYPE_MONITOR_MANAGER, MetaMonitorManager))
#define META_MONITOR_MANAGER_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), META_TYPE_MONITOR_MANAGER, MetaMonitorManagerClass))
#define META_IS_MONITOR_MANAGER(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), META_TYPE_MONITOR_MANAGER))
#define META_IS_MONITOR_MANAGER_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), META_TYPE_MONITOR_MANAGER))
#define META_MONITOR_MANAGER_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), META_TYPE_MONITOR_MANAGER, MetaMonitorManagerClass))
struct _MetaMonitorManager
{
MetaDBusDisplayConfigSkeleton parent_instance;
/* XXX: this structure is very badly
packed, but I like the logical organization
of fields */
gboolean in_init;
unsigned int serial;
MetaPowerSave power_save_mode;
int max_screen_width;
int max_screen_height;
int screen_width;
int screen_height;
/* Outputs refer to physical screens,
CRTCs refer to stuff that can drive outputs
(like encoders, but less tied to the HW),
while monitor_infos refer to logical ones.
See also the comment in monitor-private.h
*/
MetaOutput *outputs;
unsigned int n_outputs;
MetaMonitorMode *modes;
unsigned int n_modes;
MetaCRTC *crtcs;
unsigned int n_crtcs;
MetaMonitorInfo *monitor_infos;
unsigned int n_monitor_infos;
int primary_monitor_index;
int dbus_name_id;
int persistent_timeout_id;
MetaMonitorConfig *config;
GnomePnpIds *pnp_ids;
};
struct _MetaMonitorManagerClass
{
MetaDBusDisplayConfigSkeletonClass parent_class;
void (*read_current) (MetaMonitorManager *);
char* (*get_edid_file) (MetaMonitorManager *,
MetaOutput *);
GBytes* (*read_edid) (MetaMonitorManager *,
MetaOutput *);
void (*apply_configuration) (MetaMonitorManager *,
MetaCRTCInfo **,
unsigned int ,
MetaOutputInfo **,
unsigned int);
void (*set_power_save_mode) (MetaMonitorManager *,
MetaPowerSave);
void (*change_backlight) (MetaMonitorManager *,
MetaOutput *,
int);
void (*get_crtc_gamma) (MetaMonitorManager *,
MetaCRTC *,
gsize *,
unsigned short **,
unsigned short **,
unsigned short **);
void (*set_crtc_gamma) (MetaMonitorManager *,
MetaCRTC *,
gsize ,
unsigned short *,
unsigned short *,
unsigned short *);
gboolean (*handle_xevent) (MetaMonitorManager *,
XEvent *);
};
GType meta_monitor_manager_get_type (void);
void meta_monitor_manager_initialize (void);
MetaMonitorManager *meta_monitor_manager_get (void);
MetaMonitorInfo *meta_monitor_manager_get_monitor_infos (MetaMonitorManager *manager,
unsigned int *n_infos);
MetaOutput *meta_monitor_manager_get_outputs (MetaMonitorManager *manager,
unsigned int *n_outputs);
void meta_monitor_manager_get_resources (MetaMonitorManager *manager,
MetaMonitorMode **modes,
unsigned int *n_modes,
MetaCRTC **crtcs,
unsigned int *n_crtcs,
MetaOutput **outputs,
unsigned int *n_outputs);
int meta_monitor_manager_get_primary_index (MetaMonitorManager *manager);
gboolean meta_monitor_manager_handle_xevent (MetaMonitorManager *manager,
XEvent *event);
void meta_monitor_manager_get_screen_size (MetaMonitorManager *manager,
int *width,
int *height);
void meta_monitor_manager_get_screen_limits (MetaMonitorManager *manager,
int *width,
int *height);
void meta_monitor_manager_apply_configuration (MetaMonitorManager *manager,
MetaCRTCInfo **crtcs,
unsigned int n_crtcs,
MetaOutputInfo **outputs,
unsigned int n_outputs);
void meta_monitor_manager_confirm_configuration (MetaMonitorManager *manager,
gboolean ok);
#define META_TYPE_MONITOR_MANAGER_XRANDR (meta_monitor_manager_xrandr_get_type ())
#define META_MONITOR_MANAGER_XRANDR(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), META_TYPE_MONITOR_MANAGER_XRANDR, MetaMonitorManagerXrandr))
#define META_MONITOR_MANAGER_XRANDR_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), META_TYPE_MONITOR_MANAGER_XRANDR, MetaMonitorManagerXrandrClass))
#define META_IS_MONITOR_MANAGER_XRANDR(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), META_TYPE_MONITOR_MANAGER_XRANDR))
#define META_IS_MONITOR_MANAGER_XRANDR_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), META_TYPE_MONITOR_MANAGER_XRANDR))
#define META_MONITOR_MANAGER_XRANDR_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), META_TYPE_MONITOR_MANAGER_XRANDR, MetaMonitorManagerXrandrClass))
typedef struct _MetaMonitorManagerXrandrClass MetaMonitorManagerXrandrClass;
typedef struct _MetaMonitorManagerXrandr MetaMonitorManagerXrandr;
GType meta_monitor_manager_xrandr_get_type (void);
#define META_TYPE_MONITOR_CONFIG (meta_monitor_config_get_type ())
#define META_MONITOR_CONFIG(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), META_TYPE_MONITOR_CONFIG, MetaMonitorConfig))
#define META_MONITOR_CONFIG_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), META_TYPE_MONITOR_CONFIG, MetaMonitorConfigClass))
#define META_IS_MONITOR_CONFIG(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), META_TYPE_MONITOR_CONFIG))
#define META_IS_MONITOR_CONFIG_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), META_TYPE_MONITOR_CONFIG))
#define META_MONITOR_CONFIG_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), META_TYPE_MONITOR_CONFIG, MetaMonitorConfigClass))
GType meta_monitor_config_get_type (void) G_GNUC_CONST;
MetaMonitorConfig *meta_monitor_config_new (void);
gboolean meta_monitor_config_match_current (MetaMonitorConfig *config,
MetaMonitorManager *manager);
gboolean meta_monitor_config_apply_stored (MetaMonitorConfig *config,
MetaMonitorManager *manager);
void meta_monitor_config_make_default (MetaMonitorConfig *config,
MetaMonitorManager *manager);
void meta_monitor_config_update_current (MetaMonitorConfig *config,
MetaMonitorManager *manager);
void meta_monitor_config_make_persistent (MetaMonitorConfig *config);
void meta_monitor_config_restore_previous (MetaMonitorConfig *config,
MetaMonitorManager *manager);
void meta_crtc_info_free (MetaCRTCInfo *info);
void meta_output_info_free (MetaOutputInfo *info);
/* Returns true if transform causes width and height to be inverted
This is true for the odd transforms in the enum */
static inline gboolean
meta_monitor_transform_is_rotated (enum wl_output_transform transform)
{
return (transform % 2);
}
#endif

1039
src/core/monitor-xrandr.c Normal file

File diff suppressed because it is too large Load Diff

1557
src/core/monitor.c Normal file

File diff suppressed because it is too large Load Diff

View File

@ -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);

View File

@ -38,17 +38,7 @@
#include <X11/Xutil.h>
#include "stack-tracker.h"
#include "ui.h"
typedef struct _MetaMonitorInfo MetaMonitorInfo;
struct _MetaMonitorInfo
{
int number;
MetaRectangle rect;
gboolean is_primary;
gboolean in_fullscreen;
XID output; /* The primary or first output for this crtc, None if no xrandr */
};
#include "monitor-private.h"
typedef void (* MetaScreenWindowFunc) (MetaScreen *screen, MetaWindow *window,
gpointer user_data);
@ -93,6 +83,7 @@ struct _MetaScreen
MetaStack *stack;
MetaStackTracker *stack_tracker;
MetaCursorTracker *cursor_tracker;
MetaCursor current_cursor;
Window flash_window;
@ -100,10 +91,11 @@ struct _MetaScreen
Window wm_sn_selection_window;
Atom wm_sn_atom;
guint32 wm_sn_timestamp;
MetaMonitorInfo *monitor_infos;
int primary_monitor_index;
int n_monitor_infos;
int primary_monitor_index;
gboolean has_xinerama_indices;
/* Cache the current monitor */
int last_monitor_index;
@ -231,10 +223,6 @@ void meta_screen_calc_workspace_layout (MetaScreen *screen,
MetaWorkspaceLayout *layout);
void meta_screen_free_workspace_layout (MetaWorkspaceLayout *layout);
void meta_screen_resize (MetaScreen *screen,
int width,
int height);
void meta_screen_minimize_all_on_active_workspace_except (MetaScreen *screen,
MetaWindow *keep);
@ -257,4 +245,14 @@ 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,
int index);
#endif

View File

@ -45,13 +45,11 @@
#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>
#ifdef HAVE_RANDR
#include <X11/extensions/Xrandr.h>
#endif
#include <X11/Xatom.h>
#include <locale.h>
#include <string.h>
@ -76,6 +74,9 @@ static void meta_screen_sn_event (SnMonitorEvent *event,
void *user_data);
#endif
static void on_monitors_changed (MetaMonitorManager *manager,
MetaScreen *screen);
enum
{
PROP_N_WORKSPACES = 1,
@ -350,250 +351,93 @@ set_wm_icon_size_hint (MetaScreen *screen)
#undef N_VALS
}
/* The list of monitors reported by the windowing system might include
* mirrored monitors with identical bounds. Since mirrored monitors
* shouldn't be treated as separate monitors for most purposes, we
* filter them out here. (We ignore the possibility of partially
* overlapping monitors because they are rare and it's hard to come
* up with any sensible interpretation.)
*/
static void
filter_mirrored_monitors (MetaScreen *screen)
meta_screen_ensure_xinerama_indices (MetaScreen *screen)
{
int i, j;
XineramaScreenInfo *infos;
int n_infos, i, j;
/* Currently always true and simplifies things */
g_assert (screen->primary_monitor_index == 0);
if (screen->has_xinerama_indices)
return;
for (i = 1; i < screen->n_monitor_infos; i++)
screen->has_xinerama_indices = TRUE;
if (!XineramaIsActive (screen->display->xdisplay))
return;
infos = XineramaQueryScreens (screen->display->xdisplay, &n_infos);
if (n_infos <= 0 || infos == NULL)
{
/* In case we've filtered previous monitors */
screen->monitor_infos[i].number = i;
meta_XFree (infos);
return;
}
for (j = 0; j < i; j++)
for (i = 0; i < screen->n_monitor_infos; ++i)
{
for (j = 0; j < n_infos; ++j)
{
if (meta_rectangle_equal (&screen->monitor_infos[i].rect,
&screen->monitor_infos[j].rect))
{
memmove (&screen->monitor_infos[i],
&screen->monitor_infos[i + 1],
(screen->n_monitor_infos - i - 1) * sizeof (MetaMonitorInfo));
screen->n_monitor_infos--;
i--;
continue;
}
if (screen->monitor_infos[i].rect.x == infos[j].x_org &&
screen->monitor_infos[i].rect.y == infos[j].y_org &&
screen->monitor_infos[i].rect.width == infos[j].width &&
screen->monitor_infos[i].rect.height == infos[j].height)
screen->monitor_infos[i].xinerama_index = j;
}
}
meta_XFree (infos);
}
#ifdef HAVE_RANDR
static MetaMonitorInfo *
find_monitor_with_rect (MetaScreen *screen, int x, int y, int w, int h)
int
meta_screen_monitor_index_to_xinerama_index (MetaScreen *screen,
int index)
{
meta_screen_ensure_xinerama_indices (screen);
return screen->monitor_infos[index].xinerama_index;
}
int
meta_screen_xinerama_index_to_monitor_index (MetaScreen *screen,
int index)
{
MetaMonitorInfo *info;
int i;
meta_screen_ensure_xinerama_indices (screen);
for (i = 0; i < screen->n_monitor_infos; i++)
{
info = &screen->monitor_infos[i];
if (x == info->rect.x &&
y == info->rect.y &&
w == info->rect.width &&
h == info->rect.height)
return info;
}
return NULL;
if (screen->monitor_infos[i].xinerama_index == index)
return i;
return -1;
}
/* In the case of multiple outputs of a single crtc (mirroring), we consider one of the
* outputs the "main". This is the one we consider "owning" the windows, so if
* the mirroring is changed to a dual monitor setup then the windows are moved to the
* crtc that now has that main output. If one of the outputs is the primary that is
* always the main, otherwise we just use the first.
*/
static XID
find_main_output_for_crtc (MetaScreen *screen, XRRScreenResources *resources, XRRCrtcInfo *crtc)
{
XRROutputInfo *output;
RROutput primary_output;
int i;
XID res;
primary_output = XRRGetOutputPrimary (screen->display->xdisplay, screen->xroot);
res = None;
for (i = 0; i < crtc->noutput; i++)
{
output = XRRGetOutputInfo (screen->display->xdisplay, resources, crtc->outputs[i]);
if (output->connection != RR_Disconnected &&
(res == None || crtc->outputs[i] == primary_output))
res = crtc->outputs[i];
XRRFreeOutputInfo (output);
}
return res;
}
#endif
static void
reload_monitor_infos (MetaScreen *screen)
{
MetaDisplay *display;
GList *tmp;
MetaMonitorManager *manager;
{
GList *tmp;
tmp = screen->workspaces;
while (tmp != NULL)
{
MetaWorkspace *space = tmp->data;
tmp = screen->workspaces;
while (tmp != NULL)
{
MetaWorkspace *space = tmp->data;
meta_workspace_invalidate_work_area (space);
tmp = tmp->next;
}
meta_workspace_invalidate_work_area (space);
tmp = tmp->next;
}
}
/* Any previous screen->monitor_infos or screen->outputs is freed by the caller */
display = screen->display;
/* Any previous screen->monitor_infos is freed by the caller */
screen->monitor_infos = NULL;
screen->n_monitor_infos = 0;
screen->last_monitor_index = 0;
/* Xinerama doesn't have a concept of primary monitor, however XRandR
* does. However, the XRandR xinerama compat code always sorts the
* primary output first, so we rely on that here. We could use the
* native XRandR calls instead of xinerama, but that would be
* slightly problematic for _NET_WM_FULLSCREEN_MONITORS support, as
* that is defined in terms of xinerama monitor indexes.
* So, since we don't need anything in xrandr except the primary
* we can keep using xinerama and use the first monitor as the
* primary.
*/
screen->primary_monitor_index = 0;
screen->has_xinerama_indices = FALSE;
screen->display->monitor_cache_invalidated = TRUE;
if (g_getenv ("MUTTER_DEBUG_XINERAMA"))
{
meta_topic (META_DEBUG_XINERAMA,
"Pretending a single monitor has two Xinerama screens\n");
manager = meta_monitor_manager_get ();
screen->monitor_infos = g_new0 (MetaMonitorInfo, 2);
screen->n_monitor_infos = 2;
screen->monitor_infos[0].number = 0;
screen->monitor_infos[0].rect = screen->rect;
screen->monitor_infos[0].rect.width = screen->rect.width / 2;
screen->monitor_infos[0].in_fullscreen = -1;
screen->monitor_infos[1].number = 1;
screen->monitor_infos[1].rect = screen->rect;
screen->monitor_infos[1].rect.x = screen->rect.width / 2;
screen->monitor_infos[1].rect.width = screen->rect.width / 2;
screen->monitor_infos[0].in_fullscreen = -1;
}
if (screen->n_monitor_infos == 0 &&
XineramaIsActive (display->xdisplay))
{
XineramaScreenInfo *infos;
int n_infos;
int i;
n_infos = 0;
infos = XineramaQueryScreens (display->xdisplay, &n_infos);
meta_topic (META_DEBUG_XINERAMA,
"Found %d Xinerama screens on display %s\n",
n_infos, display->name);
if (n_infos > 0)
{
screen->monitor_infos = g_new0 (MetaMonitorInfo, n_infos);
screen->n_monitor_infos = n_infos;
i = 0;
while (i < n_infos)
{
screen->monitor_infos[i].number = infos[i].screen_number;
screen->monitor_infos[i].rect.x = infos[i].x_org;
screen->monitor_infos[i].rect.y = infos[i].y_org;
screen->monitor_infos[i].rect.width = infos[i].width;
screen->monitor_infos[i].rect.height = infos[i].height;
screen->monitor_infos[i].in_fullscreen = -1;
meta_topic (META_DEBUG_XINERAMA,
"Monitor %d is %d,%d %d x %d\n",
screen->monitor_infos[i].number,
screen->monitor_infos[i].rect.x,
screen->monitor_infos[i].rect.y,
screen->monitor_infos[i].rect.width,
screen->monitor_infos[i].rect.height);
++i;
}
}
meta_XFree (infos);
#ifdef HAVE_RANDR
{
XRRScreenResources *resources;
resources = XRRGetScreenResourcesCurrent (display->xdisplay, screen->xroot);
if (resources)
{
for (i = 0; i < resources->ncrtc; i++)
{
XRRCrtcInfo *crtc;
MetaMonitorInfo *info;
crtc = XRRGetCrtcInfo (display->xdisplay, resources, resources->crtcs[i]);
info = find_monitor_with_rect (screen, crtc->x, crtc->y, (int)crtc->width, (int)crtc->height);
if (info)
info->output = find_main_output_for_crtc (screen, resources, crtc);
XRRFreeCrtcInfo (crtc);
}
XRRFreeScreenResources (resources);
}
}
#endif
}
else if (screen->n_monitor_infos > 0)
{
meta_topic (META_DEBUG_XINERAMA,
"No Xinerama extension or Xinerama inactive on display %s\n",
display->name);
}
/* If no Xinerama, fill in the single screen info so
* we can use the field unconditionally
*/
if (screen->n_monitor_infos == 0)
{
meta_topic (META_DEBUG_XINERAMA,
"No Xinerama screens, using default screen info\n");
screen->monitor_infos = g_new0 (MetaMonitorInfo, 1);
screen->n_monitor_infos = 1;
screen->monitor_infos[0].number = 0;
screen->monitor_infos[0].rect = screen->rect;
screen->monitor_infos[0].in_fullscreen = -1;
}
filter_mirrored_monitors (screen);
screen->monitor_infos[screen->primary_monitor_index].is_primary = TRUE;
g_assert (screen->n_monitor_infos > 0);
g_assert (screen->monitor_infos != NULL);
screen->monitor_infos = meta_monitor_manager_get_monitor_infos (manager,
(unsigned*)&screen->n_monitor_infos);
screen->primary_monitor_index = meta_monitor_manager_get_primary_index (manager);
}
/* The guard window allows us to leave minimized windows mapped so
@ -603,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;
@ -641,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);
@ -668,6 +515,7 @@ meta_screen_new (MetaDisplay *display,
char buf[128];
guint32 manager_timestamp;
gulong current_workspace;
MetaMonitorManager *manager;
replace_current_wm = meta_get_replace_current_wm ();
@ -826,8 +674,18 @@ meta_screen_new (MetaDisplay *display,
screen->xscreen = ScreenOfDisplay (xdisplay, number);
screen->xroot = xroot;
screen->rect.x = screen->rect.y = 0;
screen->rect.width = WidthOfScreen (screen->xscreen);
screen->rect.height = HeightOfScreen (screen->xscreen);
if (!meta_is_wayland_compositor ())
meta_monitor_manager_initialize ();
manager = meta_monitor_manager_get ();
g_signal_connect (manager, "monitors-changed",
G_CALLBACK (on_monitors_changed), screen);
meta_monitor_manager_get_screen_size (manager,
&screen->rect.width,
&screen->rect.height);
screen->current_cursor = -1; /* invalid/unset */
screen->default_xvisual = DefaultVisualOfScreen (screen->xscreen);
screen->default_depth = DefaultDepthOfScreen (screen->xscreen);
@ -852,12 +710,9 @@ meta_screen_new (MetaDisplay *display,
screen->compositor_data = NULL;
screen->guard_window = None;
screen->monitor_infos = NULL;
screen->n_monitor_infos = 0;
screen->last_monitor_index = 0;
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 */
@ -941,7 +796,7 @@ meta_screen_new (MetaDisplay *display,
meta_verbose ("Added screen %d ('%s') root 0x%lx\n",
screen->number, screen->screen_name, screen->xroot);
return screen;
}
@ -1082,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);
@ -1613,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
@ -1898,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);
}
@ -2997,19 +2844,15 @@ meta_screen_resize_func (MetaScreen *screen,
meta_window_recalc_features (window);
}
void
meta_screen_resize (MetaScreen *screen,
int width,
int height)
static void
on_monitors_changed (MetaMonitorManager *manager,
MetaScreen *screen)
{
GSList *windows, *tmp;
MetaMonitorInfo *old_monitor_infos;
GSList *tmp, *windows;
screen->rect.width = width;
screen->rect.height = height;
/* Save the old monitor infos, so they stay valid during the update */
old_monitor_infos = screen->monitor_infos;
meta_monitor_manager_get_screen_size (manager,
&screen->rect.width,
&screen->rect.height);
reload_monitor_infos (screen);
set_desktop_geometry_hint (screen);
@ -3021,8 +2864,8 @@ meta_screen_resize (MetaScreen *screen,
changes.x = 0;
changes.y = 0;
changes.width = width;
changes.height = height;
changes.width = screen->rect.width;
changes.height = screen->rect.height;
XConfigureWindow(screen->display->xdisplay,
screen->guard_window,
@ -3032,7 +2875,8 @@ meta_screen_resize (MetaScreen *screen,
if (screen->display->compositor)
meta_compositor_sync_screen_size (screen->display->compositor,
screen, width, height);
screen,
screen->rect.width, screen->rect.height);
/* Queue a resize on all the windows */
meta_screen_foreach_window (screen, meta_screen_resize_func, 0);
@ -3048,7 +2892,6 @@ meta_screen_resize (MetaScreen *screen,
meta_window_update_for_monitors_changed (window);
}
g_free (old_monitor_infos);
g_slist_free (windows);
meta_screen_queue_check_fullscreen (screen);
@ -3844,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

View File

@ -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);

View File

@ -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);
}

View File

@ -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

View File

@ -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)
@ -332,6 +345,8 @@ topic_name (MetaDebugTopic topic)
return "COMPOSITOR";
case META_DEBUG_EDGE_RESISTANCE:
return "EDGE_RESISTANCE";
case META_DEBUG_DBUS:
return "DBUS";
case META_DEBUG_VERBOSE:
return "VERBOSE";
}
@ -637,8 +652,13 @@ meta_show_dialog (const char *type,
append_argument (args, "zenity");
append_argument (args, type);
append_argument (args, "--display");
append_argument (args, display);
if (display)
{
append_argument (args, "--display");
append_argument (args, display);
}
append_argument (args, "--class");
append_argument (args, "mutter-dialog");
append_argument (args, "--title");

View File

@ -44,6 +44,15 @@
#include <X11/Xutil.h>
#include <cairo.h>
#include <gdk-pixbuf/gdk-pixbuf.h>
#include "meta-wayland-private.h"
/* XXX: We should find a nicer approach to deal with the
* circular dependency we have with the current headers
* (meta-wayland-private.h which typedefs MetaWaylandSurface
* also includes window-private.h) */
#ifndef HAVE_META_WAYLAND_SURFACE_TYPE
typedef struct _MetaWaylandSurface MetaWaylandSurface;
#endif
typedef struct _MetaWindowQueue MetaWindowQueue;
@ -69,6 +78,7 @@ typedef enum {
_NET_WM_BYPASS_COMPOSITOR_HINT_OFF = 2,
} MetaBypassCompositorHintValue;
struct _MetaWindow
{
GObject parent_instance;
@ -77,6 +87,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;
@ -165,7 +179,7 @@ struct _MetaWindow
* been overridden (via a client message), the window will cover the union of
* these monitors. If not, this is the single monitor which the window's
* origin is on. */
long fullscreen_monitors[4];
gint fullscreen_monitors[4];
/* Whether we're trying to constrain the window to be fully onscreen */
guint require_fully_onscreen : 1;
@ -325,9 +339,6 @@ struct _MetaWindow
guint using_net_wm_icon_name : 1; /* vs. plain wm_icon_name */
guint using_net_wm_visible_icon_name : 1; /* tracked so we can clear it */
/* has a shape mask */
guint has_shape : 1;
/* icon props have changed */
guint need_reread_icon : 1;
@ -349,9 +360,15 @@ struct _MetaWindow
/* if non-NULL, the bounds of the window frame */
cairo_region_t *frame_bounds;
/* if non-NULL, the bounding shape region of the window */
cairo_region_t *shape_region;
/* if non-NULL, the opaque region _NET_WM_OPAQUE_REGION */
cairo_region_t *opaque_region;
/* the input shape region for picking */
cairo_region_t *input_region;
/* if TRUE, the we have the new form of sync request counter which
* also handles application frames */
guint extended_sync_request_counter : 1;
@ -490,6 +507,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 +686,6 @@ void meta_window_update_icon_now (MetaWindow *window);
void meta_window_update_role (MetaWindow *window);
void meta_window_update_net_wm_type (MetaWindow *window);
void meta_window_update_opaque_region (MetaWindow *window);
void meta_window_update_for_monitors_changed (MetaWindow *window);
void meta_window_update_on_all_workspaces (MetaWindow *window);
@ -679,4 +699,16 @@ void meta_window_compute_tile_match (MetaWindow *window);
gboolean meta_window_updates_are_frozen (MetaWindow *window);
void meta_window_set_opaque_region (MetaWindow *window,
cairo_region_t *region);
void meta_window_update_opaque_region_x11 (MetaWindow *window);
void meta_window_set_input_region (MetaWindow *window,
cairo_region_t *region);
void meta_window_update_input_region_x11 (MetaWindow *window);
void meta_window_set_shape_region (MetaWindow *window,
cairo_region_t *region);
void meta_window_update_shape_region_x11 (MetaWindow *window);
#endif

View File

@ -565,7 +565,7 @@ reload_opaque_region (MetaWindow *window,
MetaPropValue *value,
gboolean initial)
{
meta_window_update_opaque_region (window);
meta_window_update_opaque_region_x11 (window);
}
static void

View File

@ -62,6 +62,8 @@
#include <X11/extensions/Xcomposite.h>
#include "meta-wayland-private.h"
/* Windows that unmaximize to a size bigger than that fraction of the workarea
* will be scaled down to that size (while maintaining aspect ratio).
* Windows that cover an area greater then this size are automaximized on map.
@ -713,10 +715,10 @@ meta_window_new (MetaDisplay *display,
* Returns TRUE if window has been filtered out and should be ignored.
*/
static gboolean
maybe_filter_window (MetaDisplay *display,
Window xwindow,
gboolean must_be_viewable,
XWindowAttributes *attrs)
maybe_filter_xwindow (MetaDisplay *display,
Window xwindow,
gboolean must_be_viewable,
XWindowAttributes *attrs)
{
static char **filter_wm_classes = NULL;
static gboolean initialized = FALSE;
@ -812,80 +814,23 @@ meta_window_should_attach_to_parent (MetaWindow *window)
}
}
MetaWindow*
meta_window_new_with_attrs (MetaDisplay *display,
Window xwindow,
gboolean must_be_viewable,
MetaCompEffect effect,
XWindowAttributes *attrs)
static MetaWindow*
meta_window_new_shared (MetaDisplay *display,
MetaScreen *screen,
MetaWindowClientType client_type,
MetaWaylandSurface *surface,
Window xwindow,
gboolean must_be_viewable,
gulong existing_wm_state,
MetaCompEffect effect,
XWindowAttributes *attrs)
{
MetaWindow *window;
GSList *tmp;
MetaWorkspace *space;
gulong existing_wm_state;
gulong event_mask;
MetaMoveResizeFlags flags;
gboolean has_shape;
MetaScreen *screen;
g_assert (attrs != NULL);
meta_verbose ("Attempting to manage 0x%lx\n", xwindow);
if (meta_display_xwindow_is_a_no_focus_window (display, xwindow))
{
meta_verbose ("Not managing no_focus_window 0x%lx\n",
xwindow);
return NULL;
}
screen = NULL;
for (tmp = display->screens; tmp != NULL; tmp = tmp->next)
{
MetaScreen *scr = tmp->data;
if (scr->xroot == attrs->root)
{
screen = tmp->data;
break;
}
}
g_assert (screen);
/* A black list of override redirect windows that we don't need to manage: */
if (attrs->override_redirect &&
(xwindow == screen->no_focus_window ||
xwindow == screen->flash_window ||
xwindow == screen->wm_sn_selection_window ||
attrs->class == InputOnly ||
/* any windows created via meta_create_offscreen_window: */
(attrs->x == -100 && attrs->y == -100
&& attrs->width == 1 && attrs->height == 1) ||
xwindow == screen->wm_cm_selection_window ||
xwindow == screen->guard_window ||
(display->compositor &&
xwindow == XCompositeGetOverlayWindow (display->xdisplay,
screen->xroot)
)
)
) {
meta_verbose ("Not managing our own windows\n");
return NULL;
}
if (maybe_filter_window (display, xwindow, must_be_viewable, attrs))
{
meta_verbose ("Not managing filtered window\n");
return NULL;
}
/* Grab server */
meta_display_grab (display);
meta_error_trap_push (display); /* Push a trap over all of window
* creation, to reduce XSync() calls
*/
meta_verbose ("must_be_viewable = %d attrs->map_state = %d (%s)\n",
must_be_viewable,
attrs->map_state,
@ -897,126 +842,14 @@ meta_window_new_with_attrs (MetaDisplay *display,
"IsUnviewable" :
"(unknown)");
existing_wm_state = WithdrawnState;
if (must_be_viewable && attrs->map_state != IsViewable)
{
/* Only manage if WM_STATE is IconicState or NormalState */
gulong state;
/* WM_STATE isn't a cardinal, it's type WM_STATE, but is an int */
if (!(meta_prop_get_cardinal_with_atom_type (display, xwindow,
display->atom_WM_STATE,
display->atom_WM_STATE,
&state) &&
(state == IconicState || state == NormalState)))
{
meta_verbose ("Deciding not to manage unmapped or unviewable window 0x%lx\n", xwindow);
meta_error_trap_pop (display);
meta_display_ungrab (display);
return NULL;
}
existing_wm_state = state;
meta_verbose ("WM_STATE of %lx = %s\n", xwindow,
wm_state_to_string (existing_wm_state));
}
meta_error_trap_push_with_return (display);
/*
* XAddToSaveSet can only be called on windows created by a different client.
* with Mutter we want to be able to create manageable windows from within
* the process (such as a dummy desktop window), so we do not want this
* call failing to prevent the window from being managed -- wrap it in its
* own error trap (we use the _with_return() version here to ensure that
* XSync() is done on the pop, otherwise the error will not get caught).
*/
meta_error_trap_push_with_return (display);
XAddToSaveSet (display->xdisplay, xwindow);
meta_error_trap_pop_with_return (display);
event_mask = PropertyChangeMask | ColormapChangeMask;
if (attrs->override_redirect)
event_mask |= StructureNotifyMask;
/* If the window is from this client (a menu, say) we need to augment
* the event mask, not replace it. For windows from other clients,
* attrs->your_event_mask will be empty at this point.
*/
XSelectInput (display->xdisplay, xwindow, attrs->your_event_mask | event_mask);
{
unsigned char mask_bits[XIMaskLen (XI_LASTEVENT)] = { 0 };
XIEventMask mask = { XIAllMasterDevices, sizeof (mask_bits), mask_bits };
meta_core_add_old_event_mask (display->xdisplay, xwindow, &mask);
XISetMask (mask.mask, XI_Enter);
XISetMask (mask.mask, XI_Leave);
XISetMask (mask.mask, XI_FocusIn);
XISetMask (mask.mask, XI_FocusOut);
XISelectEvents (display->xdisplay, xwindow, &mask, 1);
}
has_shape = FALSE;
#ifdef HAVE_SHAPE
if (META_DISPLAY_HAS_SHAPE (display))
{
int x_bounding, y_bounding, x_clip, y_clip;
unsigned w_bounding, h_bounding, w_clip, h_clip;
int bounding_shaped, clip_shaped;
XShapeSelectInput (display->xdisplay, xwindow, ShapeNotifyMask);
XShapeQueryExtents (display->xdisplay, xwindow,
&bounding_shaped, &x_bounding, &y_bounding,
&w_bounding, &h_bounding,
&clip_shaped, &x_clip, &y_clip,
&w_clip, &h_clip);
has_shape = bounding_shaped != FALSE;
meta_topic (META_DEBUG_SHAPES,
"Window has_shape = %d extents %d,%d %u x %u\n",
has_shape, x_bounding, y_bounding,
w_bounding, h_bounding);
}
#endif
/* Get rid of any borders */
if (attrs->border_width != 0)
XSetWindowBorderWidth (display->xdisplay, xwindow, 0);
/* Get rid of weird gravities */
if (attrs->win_gravity != NorthWestGravity)
{
XSetWindowAttributes set_attrs;
set_attrs.win_gravity = NorthWestGravity;
XChangeWindowAttributes (display->xdisplay,
xwindow,
CWWinGravity,
&set_attrs);
}
if (meta_error_trap_pop_with_return (display) != Success)
{
meta_verbose ("Window 0x%lx disappeared just as we tried to manage it\n",
xwindow);
meta_error_trap_pop (display);
meta_display_ungrab (display);
return NULL;
}
window = g_object_new (META_TYPE_WINDOW, NULL);
window->constructing = TRUE;
window->dialog_pid = -1;
window->client_type = client_type;
window->surface = surface;
window->xwindow = xwindow;
/* this is in window->screen->display, but that's too annoying to
@ -1041,8 +874,6 @@ meta_window_new_with_attrs (MetaDisplay *display,
/* avoid tons of stack updates */
meta_stack_freeze (window->screen->stack);
window->has_shape = has_shape;
window->rect.x = attrs->x;
window->rect.y = attrs->y;
window->rect.width = attrs->width;
@ -1142,7 +973,11 @@ meta_window_new_with_attrs (MetaDisplay *display,
window->mwm_has_move_func = TRUE;
window->mwm_has_resize_func = TRUE;
window->decorated = TRUE;
if (client_type == META_WINDOW_CLIENT_TYPE_X11)
window->decorated = TRUE;
else
window->decorated = FALSE;
window->has_close_func = TRUE;
window->has_minimize_func = TRUE;
window->has_maximize_func = TRUE;
@ -1201,23 +1036,28 @@ meta_window_new_with_attrs (MetaDisplay *display,
window->tile_match = NULL;
if (window->override_redirect)
{
window->decorated = FALSE;
window->always_sticky = TRUE;
window->has_close_func = FALSE;
window->has_shade_func = FALSE;
window->has_move_func = FALSE;
window->has_resize_func = FALSE;
}
meta_display_register_x_window (display, &window->xwindow, window);
/* Assign this #MetaWindow a sequence number which can be used
* for sorting.
*/
window->stable_sequence = ++display->window_sequence_counter;
if (client_type == META_WINDOW_CLIENT_TYPE_X11)
{
if (window->override_redirect)
{
window->decorated = FALSE;
window->always_sticky = TRUE;
window->has_close_func = FALSE;
window->has_shade_func = FALSE;
window->has_move_func = FALSE;
window->has_resize_func = FALSE;
}
meta_display_register_x_window (display, &window->xwindow, window);
meta_window_update_shape_region_x11 (window);
meta_window_update_input_region_x11 (window);
}
/* assign the window to its group, or create a new group if needed
*/
window->group = NULL;
@ -1226,7 +1066,8 @@ meta_window_new_with_attrs (MetaDisplay *display,
meta_window_load_initial_properties (window);
if (!window->override_redirect)
if (!window->override_redirect &&
client_type == META_WINDOW_CLIENT_TYPE_X11)
{
update_sm_hints (window); /* must come after transient_for */
@ -1300,6 +1141,16 @@ meta_window_new_with_attrs (MetaDisplay *display,
if (window->decorated)
meta_window_ensure_frame (window);
if (window->client_type == META_WINDOW_CLIENT_TYPE_WAYLAND)
{
MetaStackWindow stack_window;
stack_window.any.type = META_WINDOW_CLIENT_TYPE_WAYLAND;
stack_window.wayland.meta_window = window;
meta_stack_tracker_record_add (window->screen->stack_tracker,
&stack_window,
0);
}
meta_window_grab_keys (window);
if (window->type != META_WINDOW_DOCK && !window->override_redirect)
{
@ -1500,11 +1351,14 @@ meta_window_new_with_attrs (MetaDisplay *display,
!window->initially_iconic)
unminimize_window_and_all_transient_parents (window);
meta_error_trap_pop (display); /* pop the XSync()-reducing trap */
meta_display_ungrab (display);
window->constructing = FALSE;
return window;
}
static void
display_notify_window (MetaDisplay *display, MetaWindow *window)
{
meta_display_notify_window_created (display, window);
if (window->wm_state_demands_attention)
@ -1512,6 +1366,254 @@ meta_window_new_with_attrs (MetaDisplay *display,
if (window->wm_hints_urgent)
g_signal_emit_by_name (window->display, "window-marked-urgent", window);
}
MetaWindow *
meta_window_new_for_wayland (MetaDisplay *display,
int width,
int height,
MetaWaylandSurface *surface)
{
XWindowAttributes attrs;
MetaScreen *scr = display->screens->data;
MetaWindow *window;
attrs.x = 0;
attrs.y = 0;
attrs.width = width;
attrs.height = height;
attrs.border_width = 0;
attrs.depth = 24;
attrs.visual = NULL;
attrs.root = scr->xroot;
attrs.class = InputOutput;
attrs.bit_gravity = NorthWestGravity;
attrs.win_gravity = NorthWestGravity;
attrs.backing_store = 0;
attrs.backing_planes = ~0;
attrs.backing_pixel = 0;
attrs.save_under = 0;
attrs.colormap = 0;
attrs.map_installed = 1;
attrs.map_state = IsUnmapped;
attrs.all_event_masks = ~0;
attrs.your_event_mask = 0;
attrs.do_not_propagate_mask = 0;
attrs.override_redirect = 0;
attrs.screen = scr->xscreen;
/* XXX: Note: In the Wayland case we currently still grab the
* xserver and trap X errors while creating a MetaWindow because we
* will still be making various redundant X requests (passing a
* window xid of None) until we thoroughly audit all the code to
* make sure it knows about non X based clients...
*/
/* Grab server */
meta_display_grab (display);
meta_error_trap_push (display); /* Push a trap over all of window
* creation, to reduce XSync() calls
*/
window = meta_window_new_shared (display,
scr,
META_WINDOW_CLIENT_TYPE_WAYLAND,
surface,
None,
TRUE,
WithdrawnState,
META_COMP_EFFECT_NONE,
&attrs);
meta_error_trap_pop (display); /* pop the XSync()-reducing trap */
meta_display_ungrab (display);
/* XXX: Maybe this could be called in meta_window_new_shared() but
* before splitting the X11 specific code out it came after the
* meta_display_ungrab() and we wanted to minimize the risk of
* breaking something.
*/
display_notify_window (window->display, window);
return window;
}
MetaWindow*
meta_window_new_with_attrs (MetaDisplay *display,
Window xwindow,
gboolean must_be_viewable,
MetaCompEffect effect,
XWindowAttributes *attrs)
{
MetaScreen *screen = NULL;
GSList *tmp;
gulong existing_wm_state;
MetaWindow *window;
gulong event_mask;
meta_verbose ("Attempting to manage 0x%lx\n", xwindow);
if (meta_display_xwindow_is_a_no_focus_window (display, xwindow))
{
meta_verbose ("Not managing no_focus_window 0x%lx\n",
xwindow);
return NULL;
}
for (tmp = display->screens; tmp != NULL; tmp = tmp->next)
{
MetaScreen *scr = tmp->data;
if (scr->xroot == attrs->root)
{
screen = tmp->data;
break;
}
}
g_assert (screen);
/* A black list of override redirect windows that we don't need to manage: */
if (attrs->override_redirect &&
(xwindow == screen->no_focus_window ||
xwindow == screen->flash_window ||
xwindow == screen->wm_sn_selection_window ||
attrs->class == InputOnly ||
/* any windows created via meta_create_offscreen_window: */
(attrs->x == -100 && attrs->y == -100
&& attrs->width == 1 && attrs->height == 1) ||
xwindow == screen->wm_cm_selection_window ||
xwindow == screen->guard_window ||
(display->compositor &&
xwindow == XCompositeGetOverlayWindow (display->xdisplay,
screen->xroot)
)
)
) {
meta_verbose ("Not managing our own windows\n");
return NULL;
}
if (maybe_filter_xwindow (display, xwindow, must_be_viewable, attrs))
{
meta_verbose ("Not managing filtered window\n");
return NULL;
}
/* Grab server */
meta_display_grab (display);
meta_error_trap_push (display); /* Push a trap over all of window
* creation, to reduce XSync() calls
*/
existing_wm_state = WithdrawnState;
if (must_be_viewable && attrs->map_state != IsViewable)
{
/* Only manage if WM_STATE is IconicState or NormalState */
gulong state;
/* WM_STATE isn't a cardinal, it's type WM_STATE, but is an int */
if (!(meta_prop_get_cardinal_with_atom_type (display, xwindow,
display->atom_WM_STATE,
display->atom_WM_STATE,
&state) &&
(state == IconicState || state == NormalState)))
{
meta_verbose ("Deciding not to manage unmapped or unviewable window 0x%lx\n", xwindow);
meta_error_trap_pop (display);
meta_display_ungrab (display);
return NULL;
}
existing_wm_state = state;
meta_verbose ("WM_STATE of %lx = %s\n", xwindow,
wm_state_to_string (existing_wm_state));
}
meta_error_trap_push_with_return (display);
/*
* XAddToSaveSet can only be called on windows created by a different
* client. with Mutter we want to be able to create manageable windows
* from within the process (such as a dummy desktop window), so we do not
* want this call failing to prevent the window from being managed -- wrap
* it in its own error trap (we use the _with_return() version here to
* ensure that XSync() is done on the pop, otherwise the error will not
* get caught).
*/
meta_error_trap_push_with_return (display);
XAddToSaveSet (display->xdisplay, xwindow);
meta_error_trap_pop_with_return (display);
event_mask = PropertyChangeMask | ColormapChangeMask;
if (attrs->override_redirect)
event_mask |= StructureNotifyMask;
/* If the window is from this client (a menu, say) we need to augment
* the event mask, not replace it. For windows from other clients,
* attrs->your_event_mask will be empty at this point.
*/
XSelectInput (display->xdisplay, xwindow, attrs->your_event_mask | event_mask);
{
unsigned char mask_bits[XIMaskLen (XI_LASTEVENT)] = { 0 };
XIEventMask mask = { XIAllMasterDevices, sizeof (mask_bits), mask_bits };
meta_core_add_old_event_mask (display->xdisplay, xwindow, &mask);
XISetMask (mask.mask, XI_Enter);
XISetMask (mask.mask, XI_Leave);
XISetMask (mask.mask, XI_FocusIn);
XISetMask (mask.mask, XI_FocusOut);
XISelectEvents (display->xdisplay, xwindow, &mask, 1);
}
/* Get rid of any borders */
if (attrs->border_width != 0)
XSetWindowBorderWidth (display->xdisplay, xwindow, 0);
/* Get rid of weird gravities */
if (attrs->win_gravity != NorthWestGravity)
{
XSetWindowAttributes set_attrs;
set_attrs.win_gravity = NorthWestGravity;
XChangeWindowAttributes (display->xdisplay,
xwindow,
CWWinGravity,
&set_attrs);
}
if (meta_error_trap_pop_with_return (display) != Success)
{
meta_verbose ("Window 0x%lx disappeared just as we tried to manage it\n",
xwindow);
meta_error_trap_pop (display);
meta_display_ungrab (display);
return NULL;
}
window = meta_window_new_shared (display,
screen,
META_WINDOW_CLIENT_TYPE_X11,
NULL,
xwindow,
must_be_viewable,
existing_wm_state,
effect,
attrs);
meta_error_trap_pop (display); /* pop the XSync()-reducing trap */
meta_display_ungrab (display);
/* XXX: Maybe this could be called in meta_window_new_shared() but
* before splitting the X11 specific code out it came after the
* meta_display_ungrab() and we wanted to minimize the risk of
* breaking something.
*/
display_notify_window (display, window);
return window;
}
@ -1682,6 +1784,16 @@ meta_window_unmanage (MetaWindow *window,
meta_verbose ("Unmanaging 0x%lx\n", window->xwindow);
if (window->client_type == META_WINDOW_CLIENT_TYPE_WAYLAND)
{
MetaStackWindow stack_window;
stack_window.any.type = META_WINDOW_CLIENT_TYPE_WAYLAND;
stack_window.wayland.meta_window = window;
meta_stack_tracker_record_remove (window->screen->stack_tracker,
&stack_window,
0);
}
if (window->display->compositor)
{
if (window->visible_to_compositor)
@ -1888,48 +2000,53 @@ meta_window_unmanage (MetaWindow *window,
meta_display_ungrab_window_buttons (window->display, window->xwindow);
meta_display_ungrab_focus_window_button (window->display, window);
meta_display_unregister_x_window (window->display, window->xwindow);
meta_error_trap_push (window->display);
/* Put back anything we messed up */
if (window->border_width != 0)
XSetWindowBorderWidth (window->display->xdisplay,
window->xwindow,
window->border_width);
/* No save set */
XRemoveFromSaveSet (window->display->xdisplay,
window->xwindow);
/* Even though the window is now unmanaged, we can't unselect events. This
* window might be a window from this process, like a GdkMenu, in
* which case it will have pointer events and so forth selected
* for it by GDK. There's no way to disentangle those events from the events
* we've selected. Even for a window from a different X client,
* GDK could also have selected events for it for IPC purposes, so we
* can't unselect in that case either.
*
* Similarly, we can't unselected for events on window->user_time_window.
* It might be our own GDK focus window, or it might be a window that a
* different client is using for multiple different things:
* _NET_WM_USER_TIME_WINDOW and IPC, perhaps.
*/
if (window->user_time_window != None)
if (window->client_type == META_WINDOW_CLIENT_TYPE_X11)
{
meta_display_unregister_x_window (window->display,
window->user_time_window);
window->user_time_window = None;
}
meta_display_unregister_x_window (window->display, window->xwindow);
meta_error_trap_push (window->display);
/* Put back anything we messed up */
if (window->border_width != 0)
XSetWindowBorderWidth (window->display->xdisplay,
window->xwindow,
window->border_width);
/* No save set */
XRemoveFromSaveSet (window->display->xdisplay,
window->xwindow);
/* Even though the window is now unmanaged, we can't unselect events. This
* window might be a window from this process, like a GdkMenu, in
* which case it will have pointer events and so forth selected
* for it by GDK. There's no way to disentangle those events from the events
* we've selected. Even for a window from a different X client,
* GDK could also have selected events for it for IPC purposes, so we
* can't unselect in that case either.
*
* Similarly, we can't unselected for events on window->user_time_window.
* It might be our own GDK focus window, or it might be a window that a
* different client is using for multiple different things:
* _NET_WM_USER_TIME_WINDOW and IPC, perhaps.
*/
if (window->user_time_window != None)
{
meta_display_unregister_x_window (window->display,
window->user_time_window);
window->user_time_window = None;
}
#ifdef HAVE_SHAPE
if (META_DISPLAY_HAS_SHAPE (window->display))
XShapeSelectInput (window->display->xdisplay, window->xwindow, NoEventMask);
if (META_DISPLAY_HAS_SHAPE (window->display))
XShapeSelectInput (window->display->xdisplay, window->xwindow, NoEventMask);
#endif
meta_error_trap_pop (window->display);
meta_error_trap_pop (window->display);
}
if (window->surface)
meta_wayland_surface_free (window->surface);
meta_prefs_remove_listener (prefs_changed_callback, window);
@ -2108,10 +2225,14 @@ set_net_wm_state (MetaWindow *window)
if (window->fullscreen)
{
data[0] = window->fullscreen_monitors[0];
data[1] = window->fullscreen_monitors[1];
data[2] = window->fullscreen_monitors[2];
data[3] = window->fullscreen_monitors[3];
data[0] = meta_screen_monitor_index_to_xinerama_index (window->screen,
window->fullscreen_monitors[0]);
data[1] = meta_screen_monitor_index_to_xinerama_index (window->screen,
window->fullscreen_monitors[1]);
data[2] = meta_screen_monitor_index_to_xinerama_index (window->screen,
window->fullscreen_monitors[2]);
data[3] = meta_screen_monitor_index_to_xinerama_index (window->screen,
window->fullscreen_monitors[3]);
meta_verbose ("Setting _NET_WM_FULLSCREEN_MONITORS\n");
meta_error_trap_push (window->display);
@ -4785,7 +4906,8 @@ meta_window_update_for_monitors_changed (MetaWindow *window)
{
MetaMonitorInfo *info = &window->screen->monitor_infos[i];
if (info->output == old->output)
if (info->output_id != 0 &&
info->output_id == old->output_id)
{
new = info;
break;
@ -5826,7 +5948,7 @@ meta_window_get_outer_rect (const MetaWindow *window,
if (window->has_custom_frame_extents)
{
GtkBorder *extents = &window->custom_frame_extents;
const GtkBorder *extents = &window->custom_frame_extents;
rect->x += extents->left;
rect->y += extents->top;
rect->width -= extents->left + extents->right;
@ -7130,10 +7252,14 @@ meta_window_client_message (MetaWindow *window,
meta_verbose ("_NET_WM_FULLSCREEN_MONITORS request for window '%s'\n",
window->desc);
top = event->xclient.data.l[0];
bottom = event->xclient.data.l[1];
left = event->xclient.data.l[2];
right = event->xclient.data.l[3];
top = meta_screen_xinerama_index_to_monitor_index (window->screen,
event->xclient.data.l[0]);
bottom = meta_screen_xinerama_index_to_monitor_index (window->screen,
event->xclient.data.l[1]);
left = meta_screen_xinerama_index_to_monitor_index (window->screen,
event->xclient.data.l[2]);
right = meta_screen_xinerama_index_to_monitor_index (window->screen,
event->xclient.data.l[3]);
/* source_indication = event->xclient.data.l[4]; */
meta_window_update_fullscreen_monitors (window, top, bottom, left, right);
@ -7628,14 +7754,25 @@ meta_window_update_net_wm_type (MetaWindow *window)
}
void
meta_window_update_opaque_region (MetaWindow *window)
meta_window_set_opaque_region (MetaWindow *window,
cairo_region_t *region)
{
g_clear_pointer (&window->opaque_region, cairo_region_destroy);
if (region != NULL)
window->opaque_region = cairo_region_reference (region);
if (window->display->compositor)
meta_compositor_window_shape_changed (window->display->compositor, window);
}
void
meta_window_update_opaque_region_x11 (MetaWindow *window)
{
cairo_region_t *opaque_region = NULL;
gulong *region = NULL;
int nitems;
g_clear_pointer (&window->opaque_region, cairo_region_destroy);
if (meta_prop_get_cardinal_list (window->display,
window->xwindow,
window->display->atom__NET_WM_OPAQUE_REGION,
@ -7678,13 +7815,193 @@ meta_window_update_opaque_region (MetaWindow *window)
}
out:
window->opaque_region = opaque_region;
meta_XFree (region);
meta_window_set_opaque_region (window, opaque_region);
cairo_region_destroy (opaque_region);
}
static cairo_region_t *
region_create_from_x_rectangles (const XRectangle *rects,
int n_rects)
{
int i;
cairo_rectangle_int_t *cairo_rects = g_newa (cairo_rectangle_int_t, n_rects);
for (i = 0; i < n_rects; i ++)
{
cairo_rects[i].x = rects[i].x;
cairo_rects[i].y = rects[i].y;
cairo_rects[i].width = rects[i].width;
cairo_rects[i].height = rects[i].height;
}
return cairo_region_create_rectangles (cairo_rects, n_rects);
}
void
meta_window_set_input_region (MetaWindow *window,
cairo_region_t *region)
{
g_clear_pointer (&window->input_region, cairo_region_destroy);
if (region != NULL)
window->input_region = cairo_region_reference (region);
if (window->display->compositor)
meta_compositor_window_shape_changed (window->display->compositor, window);
}
void
meta_window_update_input_region_x11 (MetaWindow *window)
{
cairo_region_t *region = NULL;
#ifdef HAVE_SHAPE
if (META_DISPLAY_HAS_SHAPE (window->display))
{
/* Translate the set of XShape rectangles that we
* get from the X server to a cairo_region. */
XRectangle *rects = NULL;
int n_rects, ordering;
int x_bounding, y_bounding, x_clip, y_clip;
unsigned w_bounding, h_bounding, w_clip, h_clip;
int bounding_shaped, clip_shaped;
meta_error_trap_push (window->display);
XShapeQueryExtents (window->display->xdisplay, window->xwindow,
&bounding_shaped, &x_bounding, &y_bounding,
&w_bounding, &h_bounding,
&clip_shaped, &x_clip, &y_clip,
&w_clip, &h_clip);
rects = XShapeGetRectangles (window->display->xdisplay,
window->xwindow,
ShapeInput,
&n_rects,
&ordering);
meta_error_trap_pop (window->display);
/* XXX: The x shape extension doesn't provide a way to only test if an
* input shape has been specified, so we have to query and throw away the
* rectangles. */
if (rects)
{
if (n_rects > 1 ||
(n_rects == 1 &&
(rects[0].x != x_bounding ||
rects[1].y != y_bounding ||
rects[2].width != w_bounding ||
rects[3].height != h_bounding)))
region = region_create_from_x_rectangles (rects, n_rects);
XFree (rects);
}
}
#endif /* HAVE_SHAPE */
if (region != NULL)
{
cairo_rectangle_int_t client_area;
client_area.x = 0;
client_area.y = 0;
client_area.width = window->rect.width;
client_area.height = window->rect.height;
/* The shape we get back from the client may have coordinates
* outside of the frame. The X SHAPE Extension requires that
* the overall shape the client provides never exceeds the
* "bounding rectangle" of the window -- the shape that the
* window would have gotten if it was unshaped. In our case,
* this is simply the client area.
*/
cairo_region_intersect_rectangle (region, &client_area);
}
meta_window_set_input_region (window, region);
cairo_region_destroy (region);
}
void
meta_window_set_shape_region (MetaWindow *window,
cairo_region_t *region)
{
g_clear_pointer (&window->shape_region, cairo_region_destroy);
if (region != NULL)
window->shape_region = cairo_region_reference (region);
if (window->display->compositor)
meta_compositor_window_shape_changed (window->display->compositor, window);
}
void
meta_window_update_shape_region_x11 (MetaWindow *window)
{
cairo_region_t *region = NULL;
#ifdef HAVE_SHAPE
if (META_DISPLAY_HAS_SHAPE (window->display))
{
/* Translate the set of XShape rectangles that we
* get from the X server to a cairo_region. */
XRectangle *rects = NULL;
int n_rects, ordering;
int x_bounding, y_bounding, x_clip, y_clip;
unsigned w_bounding, h_bounding, w_clip, h_clip;
int bounding_shaped, clip_shaped;
meta_error_trap_push (window->display);
XShapeQueryExtents (window->display->xdisplay, window->xwindow,
&bounding_shaped, &x_bounding, &y_bounding,
&w_bounding, &h_bounding,
&clip_shaped, &x_clip, &y_clip,
&w_clip, &h_clip);
if (bounding_shaped)
{
rects = XShapeGetRectangles (window->display->xdisplay,
window->xwindow,
ShapeBounding,
&n_rects,
&ordering);
}
meta_error_trap_pop (window->display);
if (rects)
{
region = region_create_from_x_rectangles (rects, n_rects);
XFree (rects);
}
}
#endif /* HAVE_SHAPE */
if (region != NULL)
{
cairo_rectangle_int_t client_area;
client_area.x = 0;
client_area.y = 0;
client_area.width = window->rect.width;
client_area.height = window->rect.height;
/* The shape we get back from the client may have coordinates
* outside of the frame. The X SHAPE Extension requires that
* the overall shape the client provides never exceeds the
* "bounding rectangle" of the window -- the shape that the
* window would have gotten if it was unshaped. In our case,
* this is simply the client area.
*/
cairo_region_intersect_rectangle (region, &client_area);
}
meta_window_set_shape_region (window, region);
cairo_region_destroy (region);
}
static void
redraw_icon (MetaWindow *window)
{
@ -8386,10 +8703,13 @@ recalc_window_features (MetaWindow *window)
case META_WINDOW_DIALOG:
case META_WINDOW_MODAL_DIALOG:
/* only skip taskbar if we have a real transient parent */
/* only skip taskbar if we have a real transient parent
(and ignore the application hints) */
if (window->xtransient_for != None &&
window->xtransient_for != window->screen->xroot)
window->skip_taskbar = TRUE;
else
window->skip_taskbar = FALSE;
break;
case META_WINDOW_NORMAL:

35
src/idle-monitor.xml Normal file
View 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>

View 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}

View File

@ -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}

View File

@ -72,6 +72,7 @@ item(_MUTTER_TIMESTAMP_PING)
item(_MUTTER_FOCUS_SET)
item(_MUTTER_SENTINEL)
item(_MUTTER_VERSION)
item(_MUTTER_PRESENTATION_OUTPUT)
item(WM_CLIENT_MACHINE)
item(MANAGER)
item(TARGETS)
@ -79,6 +80,7 @@ item(MULTIPLE)
item(TIMESTAMP)
item(VERSION)
item(ATOM_PAIR)
item(BACKLIGHT)
/* Oddities: These are used, and we need atoms for them,
* but when we need all _NET_WM hints (i.e. when we're making

View 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

View 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

View File

@ -205,6 +205,21 @@ struct _MetaPluginClass
gboolean (*keybinding_filter) (MetaPlugin *plugin,
MetaKeyBinding *binding);
/**
* MetaPluginClass::confirm_display_config:
* @plugin: a #MetaPlugin
*
* Virtual function called when the display configuration changes.
* The common way to implement this function is to show some form
* of modal dialog that should ask the user if everything was ok.
*
* When confirmed by the user, the plugin must call meta_plugin_complete_display_change()
* to make the configuration permanent. If that function is not
* called within the timeout, the previous configuration will be
* reapplied.
*/
void (*confirm_display_change) (MetaPlugin *plugin);
/**
* MetaPluginClass::plugin_info:
* @plugin: a #MetaPlugin
@ -214,6 +229,7 @@ struct _MetaPluginClass
* Returns: a #MetaPluginInfo.
*/
const MetaPluginInfo * (*plugin_info) (MetaPlugin *plugin);
};
/**
@ -360,6 +376,10 @@ void
meta_plugin_destroy_completed (MetaPlugin *plugin,
MetaWindowActor *actor);
void
meta_plugin_complete_display_change (MetaPlugin *plugin,
gboolean ok);
/**
* MetaModalOptions:
* @META_MODAL_POINTER_ALREADY_GRABBED: if set the pointer is already
@ -376,8 +396,6 @@ typedef enum {
gboolean
meta_plugin_begin_modal (MetaPlugin *plugin,
Window grab_window,
Cursor cursor,
MetaModalOptions options,
guint32 timestamp);

View File

@ -64,8 +64,6 @@ 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);
@ -75,18 +73,20 @@ void meta_shaped_texture_update_area (MetaShapedTexture *stex,
int width,
int height);
void meta_shaped_texture_set_pixmap (MetaShapedTexture *stex,
Pixmap pixmap);
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);

View File

@ -38,5 +38,6 @@ typedef struct _MetaWorkspace MetaWorkspace;
*/
typedef struct _MetaGroup MetaGroup;
typedef struct _MetaKeyBinding MetaKeyBinding;
typedef struct _MetaCursorTracker MetaCursorTracker;
#endif

View File

@ -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);
@ -100,7 +102,8 @@ typedef enum
META_DEBUG_RESIZING = 1 << 18,
META_DEBUG_SHAPES = 1 << 19,
META_DEBUG_COMPOSITOR = 1 << 20,
META_DEBUG_EDGE_RESISTANCE = 1 << 21
META_DEBUG_EDGE_RESISTANCE = 1 << 21,
META_DEBUG_DBUS = 1 << 22
} MetaDebugTopic;
void meta_topic_real (MetaDebugTopic topic,

View File

@ -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))

View File

@ -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}\"

View File

@ -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

View File

@ -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

View File

@ -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));
}

View File

@ -0,0 +1,548 @@
/*
* 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"
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;
}

View 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__ */

View File

@ -0,0 +1,512 @@
/*
* 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 "meta-wayland-keyboard.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,
keyboard->modifiers.mods_depressed,
keyboard->modifiers.mods_latched,
keyboard->modifiers.mods_locked,
keyboard->modifiers.group);
}
}
}
static const MetaWaylandKeyboardGrabInterface
default_keyboard_grab_interface = {
default_grab_key,
default_grab_modifiers,
};
gboolean
meta_wayland_keyboard_init (MetaWaylandKeyboard *keyboard,
struct wl_display *display)
{
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);
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
set_modifiers (MetaWaylandKeyboard *keyboard,
guint32 serial,
ClutterModifierType modifier_state)
{
MetaWaylandKeyboardGrab *grab = keyboard->grab;
uint32_t depressed_mods = 0;
uint32_t locked_mods = 0;
if (keyboard->last_modifier_state == modifier_state)
return;
if ((modifier_state & CLUTTER_SHIFT_MASK) &&
keyboard->xkb_info.shift_mod != XKB_MOD_INVALID)
depressed_mods |= (1 << keyboard->xkb_info.shift_mod);
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);
keyboard->last_modifier_state = modifier_state;
grab->interface->modifiers (grab,
serial,
depressed_mods,
0, /* latched_modes */
locked_mods,
0 /* 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);
wl_keyboard_send_modifiers (resource, serial,
keyboard->modifiers.mods_depressed,
keyboard->modifiers.mods_latched,
keyboard->modifiers.mods_locked,
keyboard->modifiers.group);
wl_keyboard_send_enter (resource, serial, surface->resource,
&keyboard->keys);
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);
/* XXX: What about keyboard->resource_list? */
if (keyboard->focus_resource)
wl_list_remove (&keyboard->focus_listener.link);
wl_array_release (&keyboard->keys);
}

View File

@ -0,0 +1,75 @@
/*
* 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>
#include "meta-wayland-seat.h"
gboolean
meta_wayland_keyboard_init (MetaWaylandKeyboard *keyboard,
struct wl_display *display);
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);
void
meta_wayland_keyboard_release (MetaWaylandKeyboard *keyboard);
#endif /* __META_WAYLAND_KEYBOARD_H__ */

View File

@ -0,0 +1,264 @@
/*
* 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 <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->modifiers.mods_depressed,
kbd->modifiers.mods_latched,
kbd->modifiers.mods_locked,
kbd->modifiers.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;
}

View File

@ -0,0 +1,49 @@
/*
* 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 "meta-wayland-seat.h"
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_start_grab (MetaWaylandPointer *pointer,
MetaWaylandPointerGrab *grab);
void
meta_wayland_pointer_end_grab (MetaWaylandPointer *pointer);
void
meta_wayland_pointer_set_current (MetaWaylandPointer *pointer,
MetaWaylandSurface *surface);
#endif /* __META_WAYLAND_POINTER_H__ */

View File

@ -0,0 +1,343 @@
/*
* 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/meta-cursor-tracker.h>
typedef struct _MetaWaylandCompositor MetaWaylandCompositor;
typedef struct _MetaWaylandSeat MetaWaylandSeat;
typedef struct _MetaWaylandPointer MetaWaylandPointer;
typedef struct _MetaWaylandPointerGrab MetaWaylandPointerGrab;
typedef struct _MetaWaylandPointerGrabInterface MetaWaylandPointerGrabInterface;
typedef struct _MetaWaylandKeyboard MetaWaylandKeyboard;
typedef struct _MetaWaylandKeyboardGrab MetaWaylandKeyboardGrab;
typedef struct _MetaWaylandKeyboardGrabInterface MetaWaylandKeyboardGrabInterface;
typedef struct _MetaWaylandDataOffer MetaWaylandDataOffer;
typedef struct _MetaWaylandDataSource MetaWaylandDataSource;
typedef struct
{
struct wl_resource *resource;
struct wl_signal destroy_signal;
struct wl_listener destroy_listener;
union
{
struct wl_shm_buffer *shm_buffer;
G_GNUC_BEGIN_IGNORE_DEPRECATIONS;
struct wl_buffer *legacy_buffer;
G_GNUC_END_IGNORE_DEPRECATIONS;
};
int32_t width, height;
uint32_t busy_count;
} MetaWaylandBuffer;
typedef struct
{
MetaWaylandBuffer *buffer;
struct wl_listener destroy_listener;
} MetaWaylandBufferReference;
typedef struct
{
struct wl_resource *resource;
cairo_region_t *region;
} MetaWaylandRegion;
struct _MetaWaylandSurface
{
struct wl_resource *resource;
MetaWaylandCompositor *compositor;
guint32 xid;
int x;
int y;
MetaWaylandBufferReference buffer_ref;
MetaWindow *window;
gboolean has_shell_surface;
/* All the pending state, that wl_surface.commit will apply. */
struct
{
/* wl_surface.attach */
gboolean newly_attached;
MetaWaylandBuffer *buffer;
struct wl_listener buffer_destroy_listener;
int32_t sx;
int32_t sy;
/* wl_surface.damage */
cairo_region_t *damage;
/* wl_surface.frame */
struct wl_list frame_callback_list;
} pending;
};
#ifndef HAVE_META_WAYLAND_SURFACE_TYPE
typedef struct _MetaWaylandSurface MetaWaylandSurface;
#endif
typedef struct
{
MetaWaylandSurface *surface;
struct wl_resource *resource;
struct wl_listener surface_destroy_listener;
} MetaWaylandShellSurface;
typedef struct
{
GSource source;
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;
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;
};
struct _MetaWaylandPointerGrabInterface
{
void (*focus) (MetaWaylandPointerGrab * grab,
MetaWaylandSurface * surface, wl_fixed_t x, wl_fixed_t y);
void (*motion) (MetaWaylandPointerGrab * grab,
uint32_t time, wl_fixed_t x, wl_fixed_t y);
void (*button) (MetaWaylandPointerGrab * grab,
uint32_t time, uint32_t button, uint32_t state);
};
struct _MetaWaylandPointerGrab
{
const MetaWaylandPointerGrabInterface *interface;
MetaWaylandPointer *pointer;
MetaWaylandSurface *focus;
wl_fixed_t x, y;
};
struct _MetaWaylandPointer
{
struct wl_list resource_list;
MetaWaylandSurface *focus;
struct wl_resource *focus_resource;
struct wl_listener focus_listener;
guint32 focus_serial;
struct wl_signal focus_signal;
MetaWaylandPointerGrab *grab;
MetaWaylandPointerGrab default_grab;
wl_fixed_t grab_x, grab_y;
guint32 grab_button;
guint32 grab_serial;
guint32 grab_time;
wl_fixed_t x, y;
MetaWaylandSurface *current;
struct wl_listener current_listener;
wl_fixed_t current_x, current_y;
guint32 button_count;
};
struct _MetaWaylandKeyboardGrabInterface
{
void (*key) (MetaWaylandKeyboardGrab * grab, uint32_t time,
uint32_t key, uint32_t state);
void (*modifiers) (MetaWaylandKeyboardGrab * grab, uint32_t serial,
uint32_t mods_depressed, uint32_t mods_latched,
uint32_t mods_locked, uint32_t group);
};
struct _MetaWaylandKeyboardGrab
{
const MetaWaylandKeyboardGrabInterface *interface;
MetaWaylandKeyboard *keyboard;
MetaWaylandSurface *focus;
uint32_t key;
};
typedef struct
{
struct xkb_keymap *keymap;
int keymap_fd;
size_t keymap_size;
char *keymap_area;
xkb_mod_index_t shift_mod;
xkb_mod_index_t caps_mod;
xkb_mod_index_t ctrl_mod;
xkb_mod_index_t alt_mod;
xkb_mod_index_t mod2_mod;
xkb_mod_index_t mod3_mod;
xkb_mod_index_t super_mod;
xkb_mod_index_t mod5_mod;
} MetaWaylandXkbInfo;
struct _MetaWaylandKeyboard
{
struct wl_list resource_list;
MetaWaylandSurface *focus;
struct wl_resource *focus_resource;
struct wl_listener focus_listener;
uint32_t focus_serial;
struct wl_signal focus_signal;
MetaWaylandKeyboardGrab *grab;
MetaWaylandKeyboardGrab default_grab;
uint32_t grab_key;
uint32_t grab_serial;
uint32_t grab_time;
struct wl_array keys;
struct
{
uint32_t mods_depressed;
uint32_t mods_latched;
uint32_t mods_locked;
uint32_t group;
} modifiers;
struct wl_display *display;
struct xkb_context *xkb_context;
MetaWaylandXkbInfo xkb_info;
struct xkb_rule_names xkb_names;
MetaWaylandKeyboardGrab input_method_grab;
struct wl_resource *input_method_resource;
ClutterModifierType last_modifier_state;
};
struct _MetaWaylandDataOffer
{
struct wl_resource *resource;
MetaWaylandDataSource *source;
struct wl_listener source_destroy_listener;
};
struct _MetaWaylandDataSource
{
struct wl_resource *resource;
struct wl_array mime_types;
void (*accept) (MetaWaylandDataSource * source,
uint32_t serial, const char *mime_type);
void (*send) (MetaWaylandDataSource * source,
const char *mime_type, int32_t fd);
void (*cancel) (MetaWaylandDataSource * source);
};
struct _MetaWaylandSeat
{
struct wl_list base_resource_list;
struct wl_signal destroy_signal;
uint32_t selection_serial;
MetaWaylandDataSource *selection_data_source;
struct wl_listener selection_data_source_listener;
struct wl_signal selection_signal;
struct wl_list drag_resource_list;
struct wl_client *drag_client;
MetaWaylandDataSource *drag_data_source;
struct wl_listener drag_data_source_listener;
MetaWaylandSurface *drag_focus;
struct wl_resource *drag_focus_resource;
struct wl_listener drag_focus_listener;
MetaWaylandPointerGrab drag_grab;
MetaWaylandSurface *drag_surface;
struct wl_listener drag_icon_listener;
struct wl_signal drag_icon_signal;
MetaWaylandPointer pointer;
MetaWaylandKeyboard keyboard;
struct wl_display *display;
MetaCursorTracker *cursor_tracker;
MetaWaylandSurface *sprite;
int hotspot_x, hotspot_y;
struct wl_listener sprite_destroy_listener;
ClutterActor *current_stage;
};
void meta_wayland_init (void);
void meta_wayland_finalize (void);
/* 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);
void meta_wayland_surface_free (MetaWaylandSurface *surface);
#endif /* META_WAYLAND_PRIVATE_H */

View File

@ -0,0 +1,552 @@
/*
* 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)
{
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);
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);
}

View File

@ -0,0 +1,50 @@
/*
* 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-wayland-private.h"
MetaWaylandSeat *
meta_wayland_seat_new (struct wl_display *display);
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__ */

View File

@ -0,0 +1,67 @@
/*
* 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/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);
}

View 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 */

1650
src/wayland/meta-wayland.c Normal file

File diff suppressed because it is too large Load Diff

View 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
View 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);
}

293
src/xrandr.xml Normal file
View File

@ -0,0 +1,293 @@
<!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.DisplayConfig:
@short_description: display configuration interface
This interface is used by mutter and gnome-settings-daemon
to apply multiple monitor configuration.
-->
<interface name="org.gnome.Mutter.DisplayConfig">
<!--
GetResources:
@serial: configuration serial
@crtcs: available CRTCs
@outputs: available outputs
@modes: available modes
@max_screen_width:
@max_screen_height:
Retrieves the current layout of the hardware.
@serial is an unique identifier representing the current state
of the screen. It must be passed back to ApplyConfiguration()
and will be increased for every configuration change (so that
mutter can detect that the new configuration is based on old
state).
A CRTC (CRT controller) is a logical monitor, ie a portion
of the compositor coordinate space. It might correspond
to multiple monitors, when in clone mode, but not that
it is possible to implement clone mode also by setting different
CRTCs to the same coordinates.
The number of CRTCs represent the maximum number of monitors
that can be set to expand and it is a HW constraint; if more
monitors are connected, then necessarily some will clone. This
is complementary to the concept of the encoder (not exposed in
the API), which groups outputs that necessarily will show the
same image (again a HW constraint).
A CRTC is represented by a DBus structure with the following
layout:
* u ID: the ID in the API of this CRTC
* x winsys_id: the low-level ID of this CRTC (which might
be a XID, a KMS handle or something entirely
different)
* i x, y, width, height: the geometry of this CRTC
(might be invalid if the CRTC is not in
use)
* i current_mode: the current mode of the CRTC, or -1 if this
CRTC is not used
Note: the size of the mode will always correspond
to the width and height of the CRTC
* u current_transform: the current transform (espressed according
to the wayland protocol)
* au transforms: all possible transforms
* a{sv} properties: other high-level properties that affect this
CRTC; they are not necessarily reflected in
the hardware.
No property is specified in this version of the API.
Note: all geometry information refers to the untransformed
display.
An output represents a physical screen, connected somewhere to
the computer. Floating connectors are not exposed in the API.
An output is a DBus struct with the following fields:
* u ID: the ID in the API
* x winsys_id: the low-level ID of this output (XID or KMS handle)
* i current_crtc: the CRTC that is currently driving this output,
or -1 if the output is disabled
* au possible_crtcs: all CRTCs that can control this output
* s name: the name of the connector to which the output is attached
(like VGA1 or HDMI)
* au modes: valid modes for this output
* au clones: valid clones for this output, ie other outputs that
can be assigned the same CRTC as this one; if you
want to mirror two outputs that don't have each other
in the clone list, you must configure two different
CRTCs for the same geometry
* a{sv} properties: other high-level properties that affect this
output; they are not necessarily reflected in
the hardware.
Known properties:
- "vendor" (s): (readonly) the human readable name
of the manufacturer
- "product" (s): (readonly) the human readable name
of the display model
- "serial" (s): (readonly) the serial number of this
particular hardware part
- "display-name" (s): (readonly) a human readable name
of this output, to be shown in the UI
- "backlight" (i): (readonly, use the specific interface)
the backlight value as a percentage
(-1 if not supported)
- "primary" (b): whether this output is primary
or not
- "presentation" (b): whether this output is
for presentation only
Note: properties might be ignored if not consistenly
applied to all outputs in the same clone group. In
general, it's expected that presentation or primary
outputs will not be cloned.
A mode represents a set of parameters that are applied to
each output, such as resolution and refresh rate. It is a separate
object so that it can be referenced by CRTCs and outputs.
Multiple outputs in the same CRTCs must all have the same mode.
A mode is exposed as:
* u ID: the ID in the API
* x winsys_id: the low-level ID of this mode
* u width, height: the resolution
* d frequency: refresh rate
Output and modes are read-only objects (except for output properties),
they can change only in accordance to HW changes (such as hotplugging
a monitor), while CRTCs can be changed with ApplyConfiguration().
XXX: actually, if you insist enough, you can add new modes
through xrandr command line or the KMS API, overriding what the
kernel driver and the EDID say.
Usually, it only matters with old cards with broken drivers, or
old monitors with broken EDIDs, but it happens more often with
projectors (if for example the kernel driver doesn't add the
640x480 - 800x600 - 1024x768 default modes). Probably something
that we need to handle in mutter anyway.
-->
<method name="GetResources">
<arg name="serial" direction="out" type="u" />
<arg name="crtcs" direction="out" type="a(uxiiiiiuaua{sv})" />
<arg name="outputs" direction="out" type="a(uxiausauaua{sv})" />
<arg name="modes" direction="out" type="a(uxuud)" />
<arg name="max_screen_width" direction="out" type="i" />
<arg name="max_screen_height" direction="out" type="i" />
</method>
<!--
ApplyConfiguration:
@serial: configuration serial
@persistent: whether this configuration should be saved on disk
@crtcs: new data for CRTCs
@outputs: new data for outputs
Applies the requested configuration changes.
@serial must match the serial from the last GetResources() call,
or org.freedesktop.DBus.AccessDenied will be generated.
If @persistent is true, mutter will attempt to replicate this
configuration the next time this HW layout appears.
@crtcs represents the new logical configuration, as a list
of structures containing:
- u ID: the API ID from the corresponding GetResources() call
- i new_mode: the API ID of the new mode to configure the CRTC
with, or -1 if the CRTC should be disabled
- i x, y: the new coordinates of the top left corner
the geometry will be completed with the size information
from @new_mode
- u transform: the desired transform
- au outputs: the API ID of outputs that should be assigned to
this CRTC
- a{sv} properties: properties whose value should be changed
Note: CRTCs not referenced in the array will be disabled.
@outputs represent the output property changes as:
- u ID: the API ID of the output to change
- a{sv} properties: properties whose value should be changed
Note: both for CRTCs and outputs, properties not included in
the dictionary will not be changed.
Note: unrecognized properties will have no effect, but if the
configuration change succeeds the property will be reported
by the next GetResources() call, and if @persistent is true,
it will also be saved to disk.
If the configuration is invalid according to the previous
GetResources() call, for example because a CRTC references
an output it cannot drive, or not all outputs support the
chosen mode, the error org.freedesktop.DBus.InvalidArgs will
be generated.
If the configuration cannot be applied for any other reason
(eg. the screen size would exceed texture limits), the error
org.freedesktop.DBus.Error.LimitsExceeded will be generated.
-->
<method name="ApplyConfiguration">
<arg name="serial" direction="in" type="u" />
<arg name="persistent" direction="in" type="b" />
<arg name="crtcs" direction="in" type="a(uiiiuaua{sv})" />
<arg name="outputs" direction="in" type="a(ua{sv})" />
</method>
<!--
ChangeBacklight:
@serial: configuration serial
@output: the API id of the output
@value: the new backlight value
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>
<!--
GetCrtcGamma:
@serial: configuration serial
@crtc: API id of the crtc
@red: red gamma ramp
@green: green gamma ramp
@blue: blue gamma ramp
Requests the current gamma ramps of @crtc.
-->
<method name="GetCrtcGamma">
<arg name="serial" direction="in" type="u" />
<arg name="crtc" direction="in" type="u" />
<arg name="red" direction="out" type="aq" />
<arg name="green" direction="out" type="aq" />
<arg name="blue" direction="out" type="aq" />
</method>
<!--
SetCrtcGamma:
@serial: configuration serial
@crtc: API id of the crtc
@red: red gamma ramp
@green: green gamma ramp
@blue: blue gamma ramp
Changes the gamma ramps of @crtc.
-->
<method name="SetCrtcGamma">
<arg name="serial" direction="in" type="u" />
<arg name="crtc" direction="in" type="u" />
<arg name="red" direction="in" type="aq" />
<arg name="green" direction="in" type="aq" />
<arg name="blue" direction="in" type="aq" />
</method>
<!--
PowerSaveMode:
Contains the current power saving mode for the screen, and
allows changing it.
Possible values:
- 0: on
- 1: standby
- 2: suspend
- 3: off
- -1: unknown (unsupported)
A client should not attempt to change the powersave mode
from -1 (unknown) to any other value, and viceversa.
Note that the actual effects of the different values
depend on the hardware and the kernel driver in use, and
it's perfectly possible that all values different than on
have the same effect.
Also, setting the PowerSaveMode to 3 (off) may or may
not have the same effect as disabling all outputs by
setting no CRTC on them with ApplyConfiguration(), and
may or may not cause a configuration change.
Also note that this property might become out of date
if changed through different means (for example using the
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>