Compare commits

...

139 Commits

Author SHA1 Message Date
e2c768d682 MetaWaylandSeat: reset pointer focus always when it might be different
It can happen because of a grab that the current surface changes
but the focus doesn't. Later on, when the grab ends, we would keep
the previous focus until the user goes out of the window and
back in again.
2013-08-14 18:42:07 +02:00
a67c12e873 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-14 18:42:07 +02:00
1dd31d67ab 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-14 18:42:07 +02:00
b1419e2966 wayland: implement support for plugin modality
Calling XIGrabDevice has no effect under wayland, because the
xserver is getting events from us. Instead, we need to use our
own interfaces for grabs.
At the same time, we can simplify the public API, as plugins
should always listen for events using clutter.

https://bugzilla.gnome.org/show_bug.cgi?id=705917
2013-08-14 18:31:00 +02:00
6924e760c4 MonitorManager: add a KMS backend
Using the new Cogl API to actually modeset (because we can't
use the DRM API directly without controlling buffer swap), we
can finally have a KMS monitor backend, which means full display
configuration when running on bare metal.
2013-08-14 10:03:50 +02:00
fa9e90fe25 [NOT FOR REVIEW] More debug 2013-08-14 10:03:50 +02:00
806d9ddb22 [NOT FOR REVIEW] debug stuff 2013-08-14 10:03:50 +02:00
a8425496ad [NOT FOR REVIEW] [SECURITY BUG] Allow spawning anything with mutter-launch 2013-08-14 10:03:50 +02:00
e9434732d4 MetaWayland: implement spawning dbus and gnome-session
If we launch gnome-session ourselves after setting up XWayland,
we then get gnome-settings-daemon and all the required session
stuff, which means we can run a full regular gnome session.
2013-08-14 10:03:50 +02:00
bbf07c9b17 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.
2013-08-14 10:03:50 +02:00
804c2be976 MetaWayland: or maybe don't do it for override redirect windows?
I don't know, OR windows are not appearing at all.
2013-08-14 10:03:50 +02:00
9bdcc9c418 meta-wayland: intersect the damage region with the window size before applying
According to the wayland documentation, damage outside the
window size is ignored.
This happened with xwayland+wlshm (causing a GL error when calling
TexSubImage2D), probably due to not resizing the buffer
until we receive the corresponding X event.
Might also be an off-by-one in xwayland, as the window size did
not actually change.

Note: we might want to take the configure_notify path instead,
and keep the GL/clutter size consistent with wayland rather than
X, because in the end that's what matters for events and composition.
2013-08-14 10:03:50 +02:00
cee22daf55 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)
2013-08-14 10:03:49 +02:00
63fdf8bcb8 MetaWindow: don't set focus to unmanaged window
Closing the last window causes the no-focus window to gain
focus, which causes mutter to unfocus the just closed window,
and that crashes. As focusing a window that is about to be
destroyed does not make sense, avoid a crash in this case.

Note: this is probably a reference counting bug in MetaWayland
actually.
2013-08-14 10:03:49 +02:00
62442cf0ce 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.
2013-08-14 10:03:49 +02:00
c1549af840 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.
2013-08-14 10:03:49 +02:00
8325f0eda5 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)
2013-08-14 10:03:49 +02:00
14931ed391 MetaWayland: don't destroy the output list, emit the right events instead
Destroying the output list causes toolkits to believe that all
monitors were unplugged, which then causes assertions in code
that have outside knowledge of this not having happened (like
in code using GnomeRR DBus API).
2013-08-14 10:03:49 +02:00
c06762518e 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.
2013-08-14 10:03:49 +02:00
8483dec921 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.
2013-08-14 10:03:49 +02:00
d7e970cb0a 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.
2013-08-14 10:03:49 +02:00
b383d55988 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.
2013-08-14 10:03:49 +02:00
25f82424b8 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.
2013-08-14 10:03:49 +02:00
7764e1f3ff 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.
2013-08-14 10:03:49 +02:00
ce3ec40036 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.
2013-08-14 10:03:49 +02:00
22a668f2cf MonitorManager: remove Cogl backend
It was useful initially to evaluate the Cogl API, which might
be what we will use to wrap the low-level KMS API, but it's
incomplete and just getting in the way.
2013-08-14 10:03:49 +02:00
97ff406102 MonitorManager: add support for DPMS levels
To the XRandR and dummy backend (and as usual the dummy backend
has no effect)
2013-08-14 10:03:49 +02:00
e2f06dfa8c MonitorManager: inherit directly from DisplayConfig instead of handling signals
This way we can handle properties too.
2013-08-14 10:03:49 +02:00
ee6b729196 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.
2013-08-14 10:03:49 +02:00
0d1647cbc4 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.
2013-08-14 10:03:49 +02:00
49a83138f4 Remove duplicate path for resizing clutter stage
Handle it in meta_compositor_sync_stage_size(), like we do under
X11.
2013-08-14 10:03:49 +02:00
1e3aad2d8c 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.
2013-08-14 10:03:49 +02:00
33c0c1dc18 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.
2013-08-14 10:03:49 +02:00
e261f0ac39 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.
2013-08-14 10:03:49 +02:00
528219fcbf 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.
2013-08-14 10:03:49 +02:00
d9a5f2638a 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.
2013-08-14 10:03:49 +02:00
738b0eee1d 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)
2013-08-14 10:03:49 +02:00
0480d6c898 wayland: apply stage size according to the monitor config
Still 1024x768, because we don't have a way to configure that,
but at least now it's hardcoded in the right place.
2013-08-14 10:03:48 +02:00
2777dfc533 Split monitor handling into an helper object
Create a new singleton object, MetaMonitorManager, which deals
with reading the XRandR configuration and in the future applying
the new one.
This is required because xwayland will not bind the xserver interface
until he has seen the current wl_outputs, so we can't wait
until MetaScreen is built to expose them.
2013-08-14 10:03:48 +02:00
cf296f26b1 Rework and consolidate monitor handling between MetaScreen and MetaWaylandCompositor
Consolidate all places that deal with output configuration in
MetaScreen, which gets it either from XRandR or from Cogl (which
in turn would read it from XRandR or KMS, depending on the backend).
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.
Also, now MetaWaylandCompositor listens for MetaScreen::monitor-changed
and exports the real configuration on the wayland socket.
2013-08-14 10:03:48 +02:00
b8f6801d20 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.
2013-08-14 10:03:48 +02:00
2a1c0429dd 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-14 10:03:09 +02:00
5ae9457176 wayland: fix a compiler warning
Implicit declaration of memset
2013-08-14 09:43:38 +02:00
e158500ef0 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-14 09:43:38 +02:00
b29d8046b0 MetaWayland: redirect stdin/stdout/stderr when running on bare metal
We need to spawn background processes for gnome-session and friends,
so we need to make sure they don't try to read or write from our
terminal.

https://bugzilla.gnome.org/show_bug.cgi?id=705861
2013-08-14 09:43:38 +02:00
fa3ca2bf10 Add a crash handler to restore the TTY and keyboard mode
If mutter crashes on secondary VT, it leaves you with a raw keyboard
that doesn't switch with Alt+FN and no way to get out. At least,
let's provide a decent error message that we crash and let's
restore everything to sane defaults.

https://bugzilla.gnome.org/show_bug.cgi?id=705861
2013-08-14 09:43:38 +02:00
fc42b478bd Fix handling SIGTERM while DRM-locked
And at the same time, clean up the signal handling in the regular
case.

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

https://bugzilla.gnome.org/show_bug.cgi?id=705861
2013-08-14 09:43:38 +02:00
c96fd23e79 Improve handling while VT switched
When we don't have the DRM master, there is absolutely nothing
we can do (no event processing, no video output), so emulate
the old X DRM lock with a nested GMainContext without sources.

https://bugzilla.gnome.org/show_bug.cgi?id=705861
2013-08-14 09:43:38 +02:00
045d03014d wayland: add input device handling too
Use the new hook in clutter-evdev to ask weston-launch for the
FDs of the input devices we need.

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

https://bugzilla.gnome.org/show_bug.cgi?id=705861
2013-08-14 09:43:38 +02:00
5f4121830c MetaWayland: install an X io error handler
This way can detect X disconnections correctly, crash with a core
dump and reset the tty.
2013-08-14 09:43:38 +02:00
43d499318f 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-14 09:43:12 +02:00
f2fab33551 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-14 09:43:08 +02:00
0e098249b1 Merge remote-tracking branch 'origin/master' into wayland 2013-08-13 10:44:09 -04:00
12d2e1f600 Support _GTK_FRAME_EXTENTS
https://bugzilla.gnome.org/show_bug.cgi?id=705766
2013-08-13 10:40:15 -04:00
c20b007985 Reintroduce mutter binary
I accidentally deleted one too many things.
2013-08-13 10:16:01 -04:00
ef480e9120 theme: Fix build breakage 2013-08-13 09:50:59 -04:00
8c1c77482d Remove old, deprecated utilities that nobody has used in a million years
https://bugzilla.gnome.org/show_bug.cgi?id=704437
2013-08-13 09:39:02 -04:00
e633606ca9 menu: Remove support for icon items from the window menu
We don't show these by default, and it uses deprecated API.
This also removes our only use of the stock icons, so remove
those as well.

https://bugzilla.gnome.org/show_bug.cgi?id=704437
2013-08-13 09:39:01 -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
6fdc23d0b7 Updated Norwegian bokmål translation 2013-08-08 22:14:40 +02:00
4862872c78 window-actor: Fix doc comment
https://bugzilla.gnome.org/show_bug.cgi?id=703332
2013-08-05 16:29:37 +02:00
ae2e4c5114 l10n: Update Japanese translation 2013-08-03 23:38:51 +09:00
06b5be2d13 Bump version to 3.9.5
Update NEWS.
2013-07-30 14:27:06 +02:00
56fb8a81b3 display: Export the timestamp of the event as well
When passing on keybindings, make sure to pass the timestamp
of the event as well as the deviceid and the action.

https://bugzilla.gnome.org/show_bug.cgi?id=704858
2013-07-25 14:23:51 +02:00
90a3d613ca meta-window-group: Fix previous commit 2013-07-18 16:33:50 +02:00
c2af13cf31 meta-window-group: Fix compile warning 2013-07-18 16:24:24 +02:00
b0cf0b2442 display: Fix compilation error
Whoops, I didn't mean to push that last commit, but let's
not break the build.
2013-07-17 21:04:55 -04:00
1c569c2d0e Remove application-based preference
It's hardcoded to FALSE.
2013-07-17 21:03:59 -04:00
e3855c77af meta-window-group: Use clutter's iteration API
Use the clutter iteration API instead of copying the list of children.
This is more efficent.

https://bugzilla.gnome.org/show_bug.cgi?id=703332
2013-07-17 19:37:13 +02:00
21fe5be026 display: Ignore _NET_WM_USER_TIME PropertyNotifies
These are spammy as well.

https://bugzilla.gnome.org/show_bug.cgi?id=703970
2013-07-15 12:47:46 -04:00
57bc974a57 display: Ignore XSyncAlarmNotify in meta_spew_event
https://bugzilla.gnome.org/show_bug.cgi?id=703970
2013-07-15 12:47:46 -04:00
3b51405255 main: Don't select for touch events on the stage
GNOME Shell's actors aren't touch capable, so we need to make sure that
they get the fallback pointer emulated events for now. This fixes the top
bar and other elements not working on a touchscreen without a grab.

https://bugzilla.gnome.org/show_bug.cgi?id=697192
2013-07-15 12:47:46 -04:00
73dbb4b9a5 window-actor: Remove another unused field 2013-07-15 12:20:26 -04:00
51acc3ee31 window-actor: Remove unused description
The desc field would never get filled in, as we can't have a window
actor without a MetaWindow, also, so remove the storage for the field.
2013-07-15 12:20:26 -04:00
a6f206f07c window-actor: Remove a field we don't use 2013-07-15 12:20:13 -04:00
9504fdd2cb Bump version to 3.9.4
Update NEWS.
2013-07-10 18:35:17 +02:00
b76c3312e9 Revert "background: Allow using sliced textures"
This reverts commit f743539886.

( accidentally pushed this when trying to push commit
  b7840bec7d )
2013-07-01 07:33:19 -04:00
fd7db8e6b3 Revert "background: downscale background to fit in texture limits"
This reverts commit 15e01152da.

( accidentally pushed this when trying to push commit
  b7840bec7d )
2013-07-01 07:31:25 -04:00
b7840bec7d background: Allow using sliced textures for file based backgrounds
Some cards have 2k texture limits, which can be smaller than
commonly sized backgrounds.

One way to get around this problem is to use Cogl's "sliced texture"
feature, that transparently uses several hardware textures under the hood.

This commit changes background textures loaded from file to potentially
use slicing.  Based on a patch by Jasper St. Pierre
<jstpierre@mecheye.net>.

https://bugzilla.gnome.org/show_bug.cgi?id=702283
2013-07-01 07:28:59 -04:00
f743539886 background: Allow using sliced textures
https://bugzilla.gnome.org/show_bug.cgi?id=702283
2013-07-01 07:28:32 -04:00
15e01152da background: downscale background to fit in texture limits
Some cards have 2k texture limits, which can be smaller than
commonly sized backgrounds.

This commit downscales the background in this situation, so that
it won't fail to load.

https://bugzilla.gnome.org/show_bug.cgi?id=702283
2013-07-01 07:28:32 -04:00
2103ff6a5c window: Don't force attached dialogs to be border-only
Originally attached dialogs did not have a titlebar, which the code
still assumes though it hasn't been true for a while; nowadays, the
actual look of attached dialogs is controlled by the theme.
As GTK+ recently gained the ability to set custom titlebars, we need
to support attached dialogs with either full borders (WM decorations)
or border-only (GTK+ titlebar).
Just remove the left-over assumption to make it work as expected.

https://bugzilla.gnome.org/show_bug.cgi?id=702764
2013-06-24 20:19:33 +02:00
8ab136b7ea window: Make sure override_redirect window have correct monitor info
We need to update window->monitor on override_redirect windows as well, other
wise they may end up with an invalid struct which triggers and assert when
meta_window_is_monitor_sized is called.

https://bugzilla.gnome.org/show_bug.cgi?id=702564
2013-06-24 17:32:22 +02:00
5205821fb9 window: Reuse current pointer position for monitor checks
Avoid a round trip to the xserver we already have the current position
anyway. Querying from the server on every move can cause the compositor to
stall during movement.
2013-06-23 21:24:41 +02:00
7187206ef5 screen: Allow reusing the current position when quering the monitor
Add new api (meta_screen_get_current_monitor_for_pos and
meta_screen_get_current_monitor_info_for_pos) that allow querying the monitor
without a roundtrip by reusing the passed in cursor position.
2013-06-23 21:24:41 +02:00
96221e6c04 compositor: Add an API to query if the stage is focused
gnome-shell needs to know whether the stage window is focused so
it can synchronize between stage window focus and Clutter key actor
focus. Track all X windows, even those without MetaWindows, when
tracking the focus window, and add a compositor-level API to determine
when the stage is focused.

https://bugzilla.gnome.org/show_bug.cgi?id=700735
2013-06-20 17:21:54 -04:00
7fdfbad6d4 display: Ensure that we ignore our own focus events for focus predictions
When we set the input focus, we first set the predicted window,
and then try to process focus events. But as XI_FocusOut on the
existing window comes before XI_FocusIn on the new window, we'll
see the focus out on the old window and think the focus is going
to nothing, which makes mutter think the prediction failed.

This didn't really matter as nothing paid attention to the focus
window changing, but with gnome-shell's focus rework, we'll try
and drop keyboard focus in events like these.

Fix this by making sure that we ignore focus window changes of our
own cause when updating the focus window field, by ignoring all
focus events that have a serial the same as the focus request or
lower. Note that if mutter doens't make any requests after the
focus request, this could be racy, as another client could steal
the focus, but mutter would ignore it as the serial was the same.
Bump the serial by making a dummy ChangeProperty request to a
mutter-controlled window in this case.

https://bugzilla.gnome.org/show_bug.cgi?id=701017
2013-06-20 17:21:54 -04:00
2a5b068863 compositor: Prevent an error in application code from keeping unredirect on permanently
We substract one from the unredirect counter when enable_unredirect_for_screen
gets called. It is an unsigned integer so substracting one from zero (which means enable) would overflow and thus keep it peramently enabled.

This should never happen because it means there is an unmatched
enable / disable pair somewhere. So in addition to fixing it add a
warning when this case gets triggered.

https://bugzilla.gnome.org/show_bug.cgi?id=701224
2013-06-18 22:18:38 +02:00
0c505faded make the window shadows lighter
Subtler shadows look more refined.

https://bugzilla.gnome.org/show_bug.cgi?id=702141
2013-06-18 17:26:24 +01:00
b2dd4f33f7 Bump version to 3.9.3
Update NEWS.
2013-06-18 16:40:02 +02:00
47b21b3547 Use new clutter_stage_set_paint_callback() function for after-paint notification
Commit 4f2bb583bf changed things so that the compositor used
clutter_threads_add_repaint_func_full (CLUTTER_REPAINT_FLAGS_POST_PAINT
to get after-paint notification and send _NET_WM_FRAME_DRAWN, but this
doesn't actually work, since Clutter will already have blocked for
VBlank before calling post-paint functions.

The result is that frame synced toolkits like GTK 3.8 will normally
only be able to draw every other frame.

Since ::paint doesn't work either, a new function
clutter_stage_set_paint_callback() has been added to Clutter
(and will be included in the 1.14 branch)

https://bugzilla.gnome.org/show_bug.cgi?id=698794
2013-06-03 13:21:54 -04:00
c119f98bac keybindings: Make sure events are always reported to the grab window
We have no need for normally reported events during grabs. In fact, it
might be harmful. A plugin might grab the keyboard through
meta_begin_modal_for_plugin() and then expect events to be reported to
the grab window they provide. If meanwhile this XIGrabDevice is
issued, events might start being reported normally to one other of our
windows breaking the plugin event processing.

In particular, on an empty workspace, we set input focus to our
no_focus_window. Then, if gnome-shell calls
meta_begin_modal_for_plugin() and meta_display_freeze_keyboard(), in
that order, input events will start being reported to no_focus_window.

There are two issues with this. One is that no_focus_window isn't
selecting for XI input events and thus the server discards them
completely. But even if that is fixed, events being reported to any
window other than the one gnome-shell expects - the clutter stage
window - means that events will stop reaching it.

https://bugzilla.gnome.org/show_bug.cgi?id=701219
2013-05-29 21:36:09 +02:00
d20078574e Bump version to 3.9.2
Update NEWS.
2013-05-28 17:23:25 +02:00
26bd4fde5c Updated Norwegian bokmål translation 2013-05-28 09:48:06 +02:00
2af49e503f keybindings: Grab and emit a signal when XK_ISO_Next_Group is pressed
This will make it possible to implement input source switching in
gnome-shell using the popular modifiers-only keybinding that's
implemented on the X server through an XKB option.

https://bugzilla.gnome.org/show_bug.cgi?id=697002
2013-05-27 13:56:04 +02:00
6ea6af6eb4 prefs: Track the XKB 'grp:' option in gsettings as a keybinding pref
We'll use the value of this option to establish a passive grab on the
keycode/modifier combos generating XK_ISO_Next_Group.

https://bugzilla.gnome.org/show_bug.cgi?id=697002
2013-05-27 13:55:57 +02:00
10df80762c keybindings: Add API to freeze/unfreeze the keyboard
We'll use this in gnome-shell to freeze the keyboard right before
switching input source and unfreeze it after that's finished so that
we don't lose any key events to the wrong input source.

https://bugzilla.gnome.org/show_bug.cgi?id=697001
2013-05-27 13:55:52 +02:00
f86032d700 prefs: Fix binding remaining grabbed after clearing all strokes
If a binding is updated with a clear set of strokes (effectively
disabling it) we aren't signaling that the binding changed and thus
the previous strokes will continue to be grabbed.

This fixes that and tries to do a better effort at checking if the
binding changed or not.

https://bugzilla.gnome.org/show_bug.cgi?id=697000
2013-05-27 13:55:33 +02:00
a8eb33f6fd Updated slovak translation 2013-05-25 21:58:43 +02:00
bd19de9429 compositor: Add an API to focus the stage X window
gnome-shell has traditionally just called XSetInputFocus when wanting to
set the input focus to the stage window, but this might cause strange,
hard-to-reproduce bugs because of an interference with mutter's focus
prediction. Add API to allow gnome-shell to focus the stage window that
also updates mutter's internal focus prediction state.

https://bugzilla.gnome.org/show_bug.cgi?id=700735
2013-05-24 17:43:37 -04:00
2ca2838548 display: Consolidate code calling XSetInputFocus into a new function
At the same time, rename set_focus_window and add a comment so we're
not confused about which function does what.

https://bugzilla.gnome.org/show_bug.cgi?id=700735
2013-05-24 17:43:37 -04:00
df8234c5e3 window: Properly handle focusing override redirect windows
If an app pops up an OR window and sets input focus to it, like
Steam does, we'll think the focus window is null, causing us to
think the app is not focused.

OR windows should not be special if they get input focus, where
the input focus would be set to NULL. Instead, the window should
be marked as focused.

https://bugzilla.gnome.org/show_bug.cgi?id=647706
2013-05-22 13:46:15 -04:00
d03ffd801e display: Use XI2 constants for mode/detail focus event values
This makes no functional difference, except conceptual clarity.

https://bugzilla.gnome.org/show_bug.cgi?id=647706
2013-05-22 13:46:15 -04:00
7a4c808e43 display: clean up focus_window vs expected_focus_window
Mutter previously defined display->focus_window as the window that the
server says is focused, but kept display->expected_focus_window to
indicate the window that we have requested to be focused. But it turns
out that "expected_focus_window" was almost always what we wanted.

Make MetaDisplay do a better job of tracking focus-related requests
and events, and change display->focus_window to be our best guess of
the "currently" focused window (ie, the window that will be focused at
the time when the server processes the next request we send it).

https://bugzilla.gnome.org/show_bug.cgi?id=647706
2013-05-22 13:46:15 -04:00
4f1d62170b test-focus: test program for focus window management
https://bugzilla.gnome.org/show_bug.cgi?id=647706
2013-05-22 13:46:15 -04:00
e10804727d compositor: remove the overlay_group concept
The hierarchy handling is handled in the shell by adding stuff
directly to the uiGroup, and we have a dedicated actor for
the overview there, so we don't need this anymore.

https://bugzilla.gnome.org/show_bug.cgi?id=700735
2013-05-22 18:36:05 +02:00
e430e051b7 window: Clean up the set_focused_internal function
Move things out of an indentation layer, and reshuffle
things around.

https://bugzilla.gnome.org/show_bug.cgi?id=647706
2013-05-22 12:35:04 -04:00
696d9d2fa9 window: Merge got_focus/lost_focus to a new function
Make it a static function for now, but this will be a private
function soon, replacing meta_window_lost_focus. This should
contain no functional changes, only cosmetic indentation changes,
so best viewed with ignorews=1 or -w or -b, you know the drill.

https://bugzilla.gnome.org/show_bug.cgi?id=647706
2013-05-22 12:35:04 -04:00
f6dd081acd window: Refactor "got focus" code
Just move this out to a separate function.

https://bugzilla.gnome.org/show_bug.cgi?id=647706
2013-05-22 12:35:04 -04:00
eddd6f8e9b Updated Norwegian bokmål translation 2013-05-20 14:46:56 +02:00
dfa4c7d670 compositor: Fix regression of shaded windows
Fix issues drawing shaded window shadows.

https://bugzilla.gnome.org/show_bug.cgi?id=693714
2013-05-15 16:52:28 +02:00
a487d4dd01 window: Eliminate a potential race condition with _NET_WM_MOVERESIZE
Clients using _NET_WM_MOVERESIZE to start a drag operation may encounter
a race condition if the user presses and releases a mouse button very
fast, getting "stuck" in a grab state. While this is easily fixed with
the user pressing the button or hitting Escape as the EWMH spec suggests,
its's still a bit of annoyance for users.

After starting a grab operation, check that the button is actually pressed
by the client, and if not, cancel the grab operation. This prevents the
stuck grab in a race-free way, although it requires an extra round-trip
to the server.

With client-side decorations becoming more popular, the use of
_NET_WM_MOVERESIZE is on the rise, thus this bug is seen more frequently
than before.

https://bugzilla.gnome.org/show_bug.cgi?id=699777
2013-05-14 14:46:20 -04:00
c2ecdd0524 prefs: Add support for string-array preferences
As we only had one string-array preference so far, we didn't bother
with adding a generic way to handle string-array preferences, and
just handled the preference in question explicitly. However we are
going to parse another string-array setting, so generalize the
existing code to make it reusable for that case.

https://bugzilla.gnome.org/show_bug.cgi?id=700223
2013-05-13 22:15:13 +02:00
50b9042ac2 window: Add an accessor for whether the window can close
The shell will use this to determine whether to show a close
button in the overview.

https://bugzilla.gnome.org/show_bug.cgi?id=699269
2013-05-09 15:34:37 -04:00
f5e75de330 ui: add missing delimiter in GTK-Doc comment block
g-ir-scanner will emit more warnings regarding broken GTK-Doc
syntax in the near future, which due to --warn-error being used
would break the build:

'''
ui/theme.c:1883: Warning: Meta: missing ":" at column 20:
 * @tokens_p: (out) The resulting tokens
                   ^
g-ir-scanner: compile: gcc -Wall -Wno-deprecated-declarations ...
g-ir-scanner: link: /bin/sh ../libtool --mode=link --tag=CC gcc ...
libtool: link: gcc -o /home/dieterv/gnome.org/checkout/mutter/...
<unknown>:: Fatal: Meta: warnings configured as fatal
<unknown>:: Fatal: Meta: warnings configured as fatal

make[4]: *** [Meta-3.0.gir] Error 1
'''

https://bugzilla.gnome.org/show_bug.cgi?id=699636
2013-05-04 00:23:11 +02:00
1ffe1eae4d Bump version to 3.9.1
Update NEWS.
2013-04-30 23:28:26 +02:00
970a446bd8 window: Add missing chain-up for finalize()
https://bugzilla.gnome.org/show_bug.cgi?id=698710
2013-04-29 14:58:31 +02:00
8880dffbdb background: Fix memory leak
https://bugzilla.gnome.org/show_bug.cgi?id=698710
2013-04-29 14:58:30 +02:00
5b6621811c barrier: Fix memory leak
https://bugzilla.gnome.org/show_bug.cgi?id=698710
2013-04-29 14:58:28 +02:00
c2a9ccb7e2 Let the UI layer (via the core) construct the frame mask
This essentially just moves install_corners() from the compositor, through
the core, into the UI layer where it arguably should have been anyway,
leaving behind stub functions which call through the various layers. This
removes the compositor's special knowledge of how rounded corners work,
replacing it with "ask the UI for an alpha mask".

The computation of border widths and heights changes a bit, because the
width and height used in install_corners() are the
meta_window_get_outer_rect() (which includes the visible borders but not
the invisible ones), whereas the more readily-available rectangle is the
MetaFrame.rect (which includes both). Computing the same width and height
as meta_window_get_outer_rect() involves compensating for the invisible
borders, but the UI layer is the authority on those anyway, so it seems
clearer to have it do the calculations from scratch.

Bug: https://bugzilla.gnome.org/show_bug.cgi?id=697758
Signed-off-by: Simon McVittie <simon.mcvittie@collabora.co.uk>
Reviewed-by: Jasper St. Pierre <jstpierre@mecheye.net>
2013-04-17 13:35:06 +01:00
4608cb6027 Fix use of uninitialized variables
If mutter is going to -Werror by default, then it can play footloose
and fancy free with this sorta stuff.

https://bugzilla.gnome.org/show_bug.cgi?id=698179
2013-04-17 11:58:43 +02:00
123 changed files with 19391 additions and 7387 deletions

2
.gitignore vendored
View File

@ -46,7 +46,6 @@ POTFILES
po/*.pot
50-metacity-desktop-key.xml
50-metacity-key.xml
inlinepixbufs.h
libmutter.pc
mutter
mutter-theme-viewer
@ -62,6 +61,7 @@ mutter-message
mutter-window-demo
focus-window
test-attached
test-focus
test-gravity
test-resizing
test-size-hints

View File

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

59
NEWS
View File

@ -1,3 +1,62 @@
3.9.5
=====
* Don't select for touch events on the stage [Jasper; #697192]
* Don't queue redraws for obscured regions [Adel; #703332]
* Export timestamp of global keybinding events [Bastien; #704858]
* Misc bug fixes and cleanups [Jasper, Rico; #703970]
Contributors:
Adel Gadllah, Bastien Nocera, Jasper St. Pierre, Rico Tzschichholz
3.9.4
=====
* Tweak window shadows [Allan; #702141]
* Ignore our own focus events for focus prediction [Jasper; #701017]
* Add API to query if the stage is focused [Jasper; #700735]
* Add API to query the monitor for a given position [Adel]
* Don't force attached dialogs to be border-only [Florian; #702764]
* Allow slicing of backgrounds to avoid texture size limits [Ray; #702283]
* Miscellaneous bug fixes and cleanups [Adel; #701224, #702564]
Contributors:
Allan Day, Adel Gadllah, Florian Müllner, Jasper St. Pierre, Ray Strode
3.9.3
=====
* Ensure events are always reported to the grab window [Rui; #701219]
* Use new clutter_stage_set_paint_callback() function to prevent dropping
frames with frame synced toolkits [Owen; #698794]
Contributors:
Rui Matos, Owen W. Taylor
3.9.2
=====
* Add meta_window_can_close() function [Jasper; #699269]
* Add support for string-array preferences [Florian; #700223]
* Fix a potential race condition with _NET_WM_MOVERESIZE [Jasper; #699777]
* Fix shade window action [Stef; #693714]
* Remove overlay_group [Giovanni; #700735]
* Improve tracking of the focus window [Dan, Jasper; #647706]
* Add API to freeze/unfreeze the keyboard [Rui; #697001]
* Grab and emit a signal when XK_ISO_Next_Group is pressed [Rui; #697002]
* Misc bug fixes and cleanups [Dieter, Jasper, Rui; #699636, #700735, #697000]
Contributors:
Giovanni Campagna, Rui Matos, Florian Müllner, Jasper St. Pierre,
Dieter Verfaillie, Stef Walter, Dan Winship
Translations:
Kjartan Maraas [nb], Ján Kyselica [sk]
3.9.1
=====
* Fix miscellaneous memory leaks [Pavel; #698710]
* Misc fixes and cleanups [Stef, Simon; #698179, #697758]
Contributors:
Simon McVittie, Pavel Vasin, Stef Walter
3.8.1
=====
* Fix crash when getting default font [Bastien; #696814]

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

@ -1,8 +1,8 @@
AC_PREREQ(2.50)
m4_define([mutter_major_version], [3])
m4_define([mutter_minor_version], [8])
m4_define([mutter_micro_version], [1])
m4_define([mutter_minor_version], [9])
m4_define([mutter_micro_version], [5])
m4_define([mutter_version],
[mutter_major_version.mutter_minor_version.mutter_micro_version])
@ -15,7 +15,7 @@ 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])
@ -73,8 +73,9 @@ MUTTER_PC_MODULES="
cairo >= 1.10.0
gsettings-desktop-schemas >= 3.7.3
xcomposite >= 0.2 xfixes xrender xdamage xi >= 1.6.0
$CLUTTER_PACKAGE >= 1.13.5
$CLUTTER_PACKAGE >= 1.14.3
cogl-1.0 >= 1.13.3
upower-glib > 0.9.11
"
GLIB_GSETTINGS
@ -113,14 +114,32 @@ AC_ARG_ENABLE(shape,
[disable mutter's use of the shaped window extension]),,
enable_shape=auto)
AC_ARG_ENABLE(wayland,
AC_HELP_STRING([--enable-wayland],
[Enable support for running as a hybrid X and Wayland compositor]),,
enable_wayland=no)
## Wayland support requires the xserver.xml protocol extension found in the weston
## repository but since there aren't currently established conventions for
## installing and discovering these we simply require a location to be given
## explicitly...
AC_ARG_WITH([wayland-protocols],
[AS_HELP_STRING([--with-wayland-protocols], [Location for wayland extension protocol specs])],
[
],
[])
AC_ARG_WITH([xwayland-path],
[AS_HELP_STRING([--with-xwayland-path], [Absolute path for an X Wayland server])],
[XWAYLAND_PATH="$withval"],
[XWAYLAND_PATH="$bindir/Xorg"])
AM_GLIB_GNU_GETTEXT
## here we get the flags we'll actually use
# GRegex requires Glib-2.14.0
PKG_CHECK_MODULES(ALL, glib-2.0 >= 2.14.0)
# gtk_window_set_icon_name requires gtk2+-2.6.0
PKG_CHECK_MODULES(MUTTER_MESSAGE, gtk+-3.0)
PKG_CHECK_MODULES(MUTTER_WINDOW_DEMO, gtk+-3.0)
PKG_CHECK_MODULES(MUTTER_LAUNCH, libdrm libsystemd-login)
# Unconditionally use this dir to avoid a circular dep with gnomecc
GNOME_KEYBINDINGS_KEYSDIR="${datadir}/gnome-control-center/keybindings"
@ -199,6 +218,29 @@ if test x$have_xcursor = xyes; then
AC_DEFINE(HAVE_XCURSOR, , [Building with Xcursor support])
fi
have_wayland=no
if test x$enable_wayland = "xyes"; then
WAYLAND_VERSION=0.1
AC_MSG_CHECKING([Wayland >= $WAYLAND_VERSION])
if ! $PKG_CONFIG --atleast-version $WAYLAND_VERSION wayland-server; then
AC_MSG_ERROR([wayland support enabled but no suitable wayland-server package found])
fi
AC_MSG_RESULT(yes)
AC_PATH_PROG([WAYLAND_SCANNER],[wayland-scanner],[no])
AS_IF([test "x$WAYLAND_SCANNER" = "xno"],
AC_MSG_ERROR([Could not find wayland-scanner in your PATH, required for parsing wayland extension protocols]))
AC_SUBST([WAYLAND_SCANNER])
AC_SUBST(XWAYLAND_PATH)
MUTTER_PC_MODULES="$MUTTER_PC_MODULES wayland-server libdrm"
AC_DEFINE(HAVE_WAYLAND, , [Building with Wayland support])
have_wayland=yes
fi
AM_CONDITIONAL(HAVE_WAYLAND, test x$have_wayland = "xyes")
PKG_CHECK_MODULES(MUTTER, $MUTTER_PC_MODULES)
PKG_CHECK_EXISTS([xi >= 1.6.99.1],
@ -307,9 +349,6 @@ if test "x$found_xsync" = "xyes"; then
fi
MUTTER_LIBS="$MUTTER_LIBS $XSYNC_LIBS $RANDR_LIBS $SHAPE_LIBS $X_LIBS $X_PRE_LIBS -lX11 $X_EXTRA_LIBS -lm"
MUTTER_MESSAGE_LIBS="$MUTTER_MESSAGE_LIBS $X_LIBS $X_PRE_LIBS -lX11 $X_EXTRA_LIBS"
MUTTER_WINDOW_DEMO_LIBS="$MUTTER_WINDOW_DEMO_LIBS $X_LIBS $X_PRE_LIBS -lX11 $X_EXTRA_LIBS -lm"
MUTTER_PROPS_LIBS="$MUTTER_PROPS_LIBS $X_LIBS $X_PRE_LIBS -lX11 $X_EXTRA_LIBS"
found_sm=no
case "$MUTTER_LIBS" in
@ -439,11 +478,11 @@ doc/man/Makefile
doc/reference/Makefile
doc/reference/meta-docs.sgml
src/Makefile
src/wm-tester/Makefile
src/libmutter.pc
src/mutter-plugins.pc
src/tools/Makefile
src/compositor/plugins/Makefile
protocol/Makefile
data/Makefile
po/Makefile.in
])
@ -465,6 +504,8 @@ mutter-$VERSION
source code location: ${srcdir}
compiler: ${CC}
Wayland: ${have_wayland}
Startup notification: ${have_startup_notification}
libcanberra: ${have_libcanberra}
Introspection: ${found_introspection}

7
data/Makefile.am Normal file
View File

@ -0,0 +1,7 @@
defaultcursordir = $(datadir)/mutter/cursors
dist_defaultcursor_DATA =
if HAVE_WAYLAND
dist_defaultcursor_DATA += left_ptr.png
endif

BIN
data/left_ptr.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 736 B

View File

@ -409,7 +409,6 @@ meta_prefs_get_theme
meta_prefs_get_titlebar_font
meta_prefs_get_num_workspaces
meta_prefs_get_dynamic_workspaces
meta_prefs_get_application_based
meta_prefs_get_disable_workarounds
meta_prefs_get_auto_raise
meta_prefs_get_auto_raise_delay

View File

@ -12,8 +12,8 @@ msgid ""
msgstr ""
"Project-Id-Version: mutter master\n"
"Report-Msgid-Bugs-To: http://bugzilla.gnome.org/enter_bug.cgi?product=mutter&keywords=I18N+L10N&component=general\n"
"POT-Creation-Date: 2013-03-22 10:02+0000\n"
"PO-Revision-Date: 2013-03-25 17:02+0000\n"
"POT-Creation-Date: 2013-07-30 12:29+0000\n"
"PO-Revision-Date: 2013-07-30 23:01+0900\n"
"Last-Translator: Jiro Matsuzawa <jmatsuzawa@gnome.org>\n"
"Language-Team: Japanese <gnome-translation@gnome.gr.jp>\n"
"Language: ja\n"
@ -133,7 +133,7 @@ msgstr "コマンド実行プロンプトを表示する"
#: ../src/50-mutter-system.xml.in.h:3
msgid "Show the activities overview"
msgstr "アクティビティを表示する"
msgstr "アクティビティ画面を表示する"
#: ../src/50-mutter-windows.xml.in.h:1
msgid "Windows"
@ -213,12 +213,12 @@ msgstr "画面右半分に表示する"
#. This probably means that a non-WM compositor like xcompmgr is running;
#. * we have no way to get it to exit
#: ../src/compositor/compositor.c:568
#: ../src/compositor/compositor.c:589
#, c-format
msgid "Another compositing manager is already running on screen %i on display \"%s\"."
msgstr "既に別の合成マネージャーがディスプレイ \"%2$s\" 上のスクリーン %1$i で起動中です"
#: ../src/compositor/meta-background.c:1191
#: ../src/compositor/meta-background.c:1076
msgid "background texture could not be created from file"
msgstr ""
@ -252,22 +252,22 @@ msgstr "待機する(_W)"
msgid "_Force Quit"
msgstr "強制終了する(_F)"
#: ../src/core/display.c:401
#: ../src/core/display.c:421
#, c-format
msgid "Missing %s extension required for compositing"
msgstr "ウィンドウの合成に必要な %s という拡張モジュールが存在しません"
#: ../src/core/display.c:493
#: ../src/core/display.c:513
#, c-format
msgid "Failed to open X Window System display '%s'\n"
msgstr "X Window System のディスプレイ '%s' のオープンに失敗しました\n"
#: ../src/core/keybindings.c:935
#: ../src/core/keybindings.c:1138
#, c-format
msgid "Some other program is already using the key %s with modifiers %x as a binding\n"
msgstr "既にバインディングとして別のプログラムでキー %s (修飾キー %x) を使っています\n"
#: ../src/core/keybindings.c:1135
#: ../src/core/keybindings.c:1335
#, fuzzy, c-format
msgid "\"%s\" is not a valid accelerator\n"
msgstr "\"%s\" はフォーカス属性のためには有効な値ではありません"
@ -328,26 +328,26 @@ msgstr "バージョンを表示する"
msgid "Mutter plugin to use"
msgstr "使用する Mutter のプラグイン"
#: ../src/core/prefs.c:1095
#: ../src/core/prefs.c:1202
msgid "Workarounds for broken applications disabled. Some applications may not behave properly.\n"
msgstr "仕様に準拠していないアプリケーションに対する次善策は無効になっています。一部のアプリケーションは正常に動作しない可能性があります\n"
#: ../src/core/prefs.c:1170
#: ../src/core/prefs.c:1277
#, c-format
msgid "Could not parse font description \"%s\" from GSettings key %s\n"
msgstr "GSettings の %2$s キーからフォント名 \"%1$s\" を解析できませんでした\n"
#: ../src/core/prefs.c:1236
#: ../src/core/prefs.c:1343
#, c-format
msgid "\"%s\" found in configuration database is not a valid value for mouse button modifier\n"
msgstr "設定データベース中の \"%s\" はマウスボタンの修飾キーとして妥当な値ではありません\n"
#: ../src/core/prefs.c:1788
#: ../src/core/prefs.c:1909
#, c-format
msgid "\"%s\" found in configuration database is not a valid value for keybinding \"%s\"\n"
msgstr "設定データベース中の \"%s\" はキーバインド \"%s\" に有効な値ではありません\n"
#: ../src/core/prefs.c:1887
#: ../src/core/prefs.c:1999
#, c-format
msgid "Workspace %d"
msgstr "ワークスペース %d"
@ -465,7 +465,7 @@ msgid "Window manager error: "
msgstr "ウィンドウマネージャーのエラー: "
#. first time through
#: ../src/core/window.c:7596
#: ../src/core/window.c:7513
#, c-format
msgid "Window %s sets SM_CLIENT_ID on itself, instead of on the WM_CLIENT_LEADER window as specified in the ICCCM.\n"
msgstr "ウィンドウ %s は ICCCM で指定されていたような WM_CLIENT_LEADER ウィンドウの代わりに自分自身で SM_CLIENT_ID を設定しています\n"
@ -477,7 +477,7 @@ msgstr "ウィンドウ %s は ICCCM で指定されていたような WM_CLIENT
#. * MWM but not WM_NORMAL_HINTS are basically broken. We complain
#. * about these apps but make them work.
#.
#: ../src/core/window.c:8320
#: ../src/core/window.c:8237
#, c-format
msgid "Window %s sets an MWM hint indicating it isn't resizable, but sets min size %d x %d and max size %d x %d; this doesn't make much sense.\n"
msgstr "ウィンドウ %s はリサイズ可能ではない MWM ヒント指示を設定していますが、最小サイズ %d x %d と最大サイズ %d x %dも設定しています。これはあまり意味がありません\n"

View File

@ -4,10 +4,10 @@
#
msgid ""
msgstr ""
"Project-Id-Version: mutter 3.8.x\n"
"Project-Id-Version: mutter 3.9.x\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2013-04-03 14:10+0200\n"
"PO-Revision-Date: 2013-04-03 14:11+0200\n"
"POT-Creation-Date: 2013-08-08 22:14+0200\n"
"PO-Revision-Date: 2013-05-28 09:48+0200\n"
"Last-Translator: Kjartan Maraas <kmaraas@gnome.org>\n"
"Language-Team: Norwegian bokmål <i18n-no@lister.ping.uio.no>\n"
"Language: \n"
@ -205,14 +205,14 @@ msgstr "Visning delt til høyre"
#. This probably means that a non-WM compositor like xcompmgr is running;
#. * we have no way to get it to exit
#: ../src/compositor/compositor.c:568
#: ../src/compositor/compositor.c:589
#, c-format
msgid ""
"Another compositing manager is already running on screen %i on display \"%s"
"\"."
msgstr "En annen compositing manager kjører skjerm %i på display «%s»."
#: ../src/compositor/meta-background.c:1064
#: ../src/compositor/meta-background.c:1076
msgid "background texture could not be created from file"
msgstr "bakgrunnstekstur kunne ikke lages fra fil"
@ -250,17 +250,17 @@ msgstr "_Vent"
msgid "_Force Quit"
msgstr "_Tvungen nedstenging"
#: ../src/core/display.c:401
#: ../src/core/display.c:421
#, c-format
msgid "Missing %s extension required for compositing"
msgstr "Mangler utvidelsen %s som kreves for komposittfunksjon"
#: ../src/core/display.c:493
#: ../src/core/display.c:513
#, c-format
msgid "Failed to open X Window System display '%s'\n"
msgstr "Feil under åpning av X Window System skjerm «%s»\n"
#: ../src/core/keybindings.c:935
#: ../src/core/keybindings.c:1138
#, c-format
msgid ""
"Some other program is already using the key %s with modifiers %x as a "
@ -269,7 +269,7 @@ msgstr ""
"Et annet program bruker allerede nøkkelen %s med modifikatorer %x som "
"binding\n"
#: ../src/core/keybindings.c:1135
#: ../src/core/keybindings.c:1335
#, c-format
msgid "\"%s\" is not a valid accelerator\n"
msgstr "«%s» er ikke en gyldig aksellerator\n"
@ -333,7 +333,7 @@ msgstr "Skriv versjonsnummer"
msgid "Mutter plugin to use"
msgstr "Mutter-tillegg som skal brukes"
#: ../src/core/prefs.c:1095
#: ../src/core/prefs.c:1202
msgid ""
"Workarounds for broken applications disabled. Some applications may not "
"behave properly.\n"
@ -341,12 +341,12 @@ msgstr ""
"Funksjonalitet for å gå rundt ødelagte programmer er deaktivert. Noen "
"programmer vil kanskje ikke oppføre seg korrekt.\n"
#: ../src/core/prefs.c:1170
#: ../src/core/prefs.c:1277
#, c-format
msgid "Could not parse font description \"%s\" from GSettings key %s\n"
msgstr "Kunne ikke tolke skriftbeskrivelsen «%s» fra GSettings-nøkkel %s\n"
#: ../src/core/prefs.c:1236
#: ../src/core/prefs.c:1343
#, c-format
msgid ""
"\"%s\" found in configuration database is not a valid value for mouse button "
@ -355,7 +355,7 @@ msgstr ""
"«%s» funnet i konfigurasjonsdatabasen er ikke en gyldig verdi for endring av "
"musknapp\n"
#: ../src/core/prefs.c:1788
#: ../src/core/prefs.c:1909
#, c-format
msgid ""
"\"%s\" found in configuration database is not a valid value for keybinding "
@ -364,7 +364,7 @@ msgstr ""
"«%s» funnet i konfigurasjonsdatabasen er ikke en gyldig verdi for "
"tastaturbinding «%s»\n"
#: ../src/core/prefs.c:1887
#: ../src/core/prefs.c:1999
#, c-format
msgid "Workspace %d"
msgstr "Arbeidsområde %d"
@ -492,7 +492,7 @@ msgid "Window manager error: "
msgstr "Feil i vindushåndterer: "
#. first time through
#: ../src/core/window.c:7596
#: ../src/core/window.c:7513
#, c-format
msgid ""
"Window %s sets SM_CLIENT_ID on itself, instead of on the WM_CLIENT_LEADER "
@ -508,7 +508,7 @@ msgstr ""
#. * MWM but not WM_NORMAL_HINTS are basically broken. We complain
#. * about these apps but make them work.
#.
#: ../src/core/window.c:8320
#: ../src/core/window.c:8237
#, c-format
msgid ""
"Window %s sets an MWM hint indicating it isn't resizable, but sets min size "
@ -684,7 +684,9 @@ msgstr "Maksimer vinduer automatisk hvis de er nesten like store som skjermen"
msgid ""
"If enabled, new windows that are initially the size of the monitor "
"automatically get maximized."
msgstr "Nye vinduer som i utgangspunktet er samme størrelse som skjermen vil automatisk bli maksimert hvis denne slås på."
msgstr ""
"Nye vinduer som i utgangspunktet er samme størrelse som skjermen vil "
"automatisk bli maksimert hvis denne slås på."
#: ../src/org.gnome.mutter.gschema.xml.in.h:19
msgid "Select window from tab popup"

480
po/sk.po

File diff suppressed because it is too large Load Diff

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

@ -3,7 +3,7 @@
lib_LTLIBRARIES = libmutter.la
SUBDIRS=wm-tester tools compositor/plugins
SUBDIRS=compositor/plugins
INCLUDES= \
-DCLUTTER_ENABLE_EXPERIMENTAL_API \
@ -29,10 +29,25 @@ INCLUDES= \
-DMUTTER_PLUGIN_DIR=\"@MUTTER_PLUGIN_DIR@\" \
-DGETTEXT_PACKAGE=\"$(GETTEXT_PACKAGE)\"
if HAVE_WAYLAND
INCLUDES += \
-I$(srcdir)/wayland \
-DXWAYLAND_PATH='"@XWAYLAND_PATH@"'
endif
mutter_built_sources = \
$(dbus_xrandr_built_sources) \
$(dbus_idle_built_sources) \
mutter-enum-types.h \
mutter-enum-types.c
if HAVE_WAYLAND
mutter_built_sources += \
wayland/xserver-protocol.c \
wayland/xserver-server-protocol.h \
wayland/xserver-client-protocol.h
endif
libmutter_la_SOURCES = \
core/async-getprop.c \
core/async-getprop.h \
@ -110,6 +125,16 @@ 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-kms.c \
core/monitor-private.h \
core/monitor-xrandr.c \
core/mutter-Xatomtype.h \
core/place.c \
core/place.h \
@ -139,7 +164,6 @@ libmutter_la_SOURCES = \
meta/common.h \
core/core.h \
ui/ui.h \
inlinepixbufs.h \
ui/frames.c \
ui/frames.h \
ui/menu.c \
@ -157,10 +181,30 @@ libmutter_la_SOURCES = \
meta/theme.h \
ui/theme-private.h \
ui/ui.c \
meta/preview-widget.h \
ui/preview-widget.c \
$(mutter_built_sources)
if HAVE_WAYLAND
libmutter_la_SOURCES += \
wayland/meta-tty.c \
wayland/meta-tty.h \
wayland/meta-wayland.c \
wayland/meta-wayland-private.h \
wayland/meta-xwayland-private.h \
wayland/meta-xwayland.c \
wayland/meta-wayland-data-device.c \
wayland/meta-wayland-data-device.h \
wayland/meta-wayland-keyboard.c \
wayland/meta-wayland-keyboard.h \
wayland/meta-wayland-pointer.c \
wayland/meta-wayland-pointer.h \
wayland/meta-wayland-seat.c \
wayland/meta-wayland-seat.h \
wayland/meta-wayland-stage.h \
wayland/meta-wayland-stage.c \
wayland/meta-weston-launch.c \
wayland/meta-weston-launch.h
endif
libmutter_la_LDFLAGS = -no-undefined
libmutter_la_LIBADD = $(MUTTER_LIBS)
@ -181,6 +225,8 @@ libmutterinclude_base_headers = \
meta/meta-background-actor.h \
meta/meta-background-group.h \
meta/meta-background.h \
meta/meta-cursor-tracker.h \
meta/meta-idle-monitor.h \
meta/meta-plugin.h \
meta/meta-shaped-texture.h \
meta/meta-shadow-factory.h \
@ -196,7 +242,6 @@ libmutterinclude_base_headers = \
# Excluded from scanning for introspection but installed
# atomnames.h: macros cause problems for scanning process
libmutterinclude_extra_headers = \
meta/preview-widget.h \
meta/atomnames.h
libmutterincludedir = $(includedir)/mutter/meta
@ -205,14 +250,24 @@ libmutterinclude_HEADERS = \
$(libmutterinclude_base_headers) \
$(libmutterinclude_extra_headers)
mutter_theme_viewer_SOURCES= \
ui/theme-viewer.c
bin_PROGRAMS=mutter mutter-theme-viewer
bin_PROGRAMS=mutter
mutter_SOURCES = core/mutter.c
mutter_LDADD = $(MUTTER_LIBS) libmutter.la
if HAVE_WAYLAND
bin_PROGRAMS+=mutter-launch
mutter_launch_SOURCES = wayland/weston-launch.c wayland/weston-launch.h
mutter_launch_CFLAGS = $(MUTTER_LAUNCH_CFLAGS)
mutter_launch_LDFLAGS = $(MUTTER_LAUNCH_LIBS) -lpam
install-exec-hook:
-chown root $(DESTDIR)$(bindir)/mutter-launch
-chmod u+s $(DESTDIR)$(bindir)/mutter-launch
endif
if HAVE_INTROSPECTION
include $(INTROSPECTION_MAKEFILE)
@ -246,8 +301,6 @@ Meta-$(api_version).gir: libmutter.la
endif
mutter_theme_viewer_LDADD= $(MUTTER_LIBS) libmutter.la
testboxes_SOURCES = core/testboxes.c
testgradient_SOURCES = ui/testgradient.c
testasyncgetprop_SOURCES = core/testasyncgetprop.c
@ -284,14 +337,7 @@ gsettings_SCHEMAS = org.gnome.mutter.gschema.xml
convertdir = $(datadir)/GConf/gsettings
convert_DATA = mutter-schemas.convert
IMAGES=stock_maximize.png stock_minimize.png stock_delete.png
VARIABLES=stock_maximize_data $(srcdir)/stock_maximize.png \
stock_minimize_data $(srcdir)/stock_minimize.png \
stock_delete_data $(srcdir)/stock_delete.png
BUILT_SOURCES = inlinepixbufs.h
CLEANFILES = \
inlinepixbufs.h \
mutter.desktop \
mutter-wm.desktop \
org.gnome.mutter.gschema.xml \
@ -300,9 +346,6 @@ CLEANFILES = \
$(typelib_DATA) \
$(gir_DATA)
inlinepixbufs.h: $(IMAGES)
$(GDK_PIXBUF_CSOURCE) --raw --build-list $(VARIABLES) >$(srcdir)/inlinepixbufs.h
pkgconfigdir = $(libdir)/pkgconfig
pkgconfig_DATA = libmutter.pc mutter-plugins.pc
@ -320,7 +363,7 @@ EXTRA_DIST=$(desktopfiles_files) \
mutter-enum-types.h.in \
mutter-enum-types.c.in
BUILT_SOURCES += $(mutter_built_sources)
BUILT_SOURCES = $(mutter_built_sources)
MUTTER_STAMP_FILES = stamp-mutter-enum-types.h
CLEANFILES += $(MUTTER_STAMP_FILES)
@ -342,3 +385,31 @@ 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 \
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 \
idle-monitor.xml
if HAVE_WAYLAND
wayland/%-protocol.c : $(top_builddir)/protocol/%.xml
$(AM_V_GEN)$(WAYLAND_SCANNER) code < $< > $@
wayland/%-server-protocol.h : $(top_builddir)/protocol/%.xml
$(AM_V_GEN)$(WAYLAND_SCANNER) server-header < $< > $@
wayland/%-client-protocol.h : $(top_builddir)/protocol/%.xml
$(AM_V_GEN)$(WAYLAND_SCANNER) client-header < $< > $@
endif

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

@ -53,17 +53,15 @@
*
* # Containers #
*
* There's three containers in the stage that can be used to place actors, here
* There's two containers in the stage that are used to place window actors, here
* are listed in the order in which they are painted:
*
* - window group, accessible with meta_get_window_group_for_screen()
* - top window group, accessible with meta_get_top_window_group_for_screen()
* - overlay group, accessible with meta_get_overlay_group_for_screen()
*
* Mutter will place actors representing windows in the window group, except for
* override-redirect windows (ie. popups and menus) which will be placed in the
* top window group. Mutter won't put any actors in the overlay group, but it's
* intended for compositors to place there panel, dashes, status bars, etc.
* top window group.
*/
#include <config.h>
@ -86,6 +84,11 @@
#include "meta-window-group.h"
#include "window-private.h" /* to check window->hidden */
#include "display-private.h" /* for meta_display_lookup_x_window() */
#ifdef HAVE_WAYLAND
#include "meta-wayland-private.h"
#include "meta-wayland-pointer.h"
#include "meta-wayland-keyboard.h"
#endif
#include <X11/extensions/shape.h>
#include <X11/extensions/Xcomposite.h>
@ -174,7 +177,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
@ -255,23 +258,6 @@ meta_get_stage_for_screen (MetaScreen *screen)
return info->stage;
}
/**
* meta_get_overlay_group_for_screen:
* @screen: a #MetaScreen
*
* Returns: (transfer none): The overlay group corresponding to @screen
*/
ClutterActor *
meta_get_overlay_group_for_screen (MetaScreen *screen)
{
MetaCompScreen *info = meta_screen_get_compositor_data (screen);
if (!info)
return NULL;
return info->overlay_group;
}
/**
* meta_get_window_group_for_screen:
* @screen: a #MetaScreen
@ -346,29 +332,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
@ -388,28 +382,67 @@ meta_empty_stage_input_region (MetaScreen *screen)
meta_set_stage_input_region (screen, region);
}
gboolean
meta_begin_modal_for_plugin (MetaScreen *screen,
MetaPlugin *plugin,
Window grab_window,
Cursor cursor,
MetaModalOptions options,
guint32 timestamp)
void
meta_focus_stage_window (MetaScreen *screen,
guint32 timestamp)
{
/* To some extent this duplicates code in meta_display_begin_grab_op(), but there
* are significant differences in how we handle grabs that make it difficult to
* merge the two.
*/
MetaDisplay *display = meta_screen_get_display (screen);
Display *xdpy = meta_display_get_xdisplay (display);
MetaCompositor *compositor = display->compositor;
gboolean pointer_grabbed = FALSE;
gboolean keyboard_grabbed = FALSE;
int result;
ClutterStage *stage;
Window window;
if (compositor->modal_plugin != NULL || display->grab_op != META_GRAB_OP_NONE)
stage = CLUTTER_STAGE (meta_get_stage_for_screen (screen));
if (!stage)
return;
if (!meta_is_wayland_compositor ())
{
window = clutter_x11_get_stage_window (stage);
if (window == None)
return;
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;
stage = CLUTTER_STAGE (meta_get_stage_for_screen (screen));
if (!stage)
return FALSE;
return (screen->display->focus_type == META_FOCUS_STAGE);
}
static gboolean
begin_modal_x11 (MetaScreen *screen,
MetaPlugin *plugin,
MetaModalOptions options,
guint32 timestamp)
{
MetaDisplay *display = meta_screen_get_display (screen);
Display *xdpy = meta_display_get_xdisplay (display);
MetaCompScreen *info = meta_screen_get_compositor_data (screen);
Window grab_window = clutter_x11_get_stage_window (CLUTTER_STAGE (info->stage));
Cursor cursor = None;
int result;
gboolean pointer_grabbed = FALSE;
gboolean keyboard_grabbed = FALSE;
if ((options & META_MODAL_POINTER_ALREADY_GRABBED) == 0)
{
unsigned char mask_bits[XIMaskLen (XI_LASTEVENT)] = { 0 };
@ -458,14 +491,6 @@ meta_begin_modal_for_plugin (MetaScreen *screen,
keyboard_grabbed = TRUE;
}
display->grab_op = META_GRAB_OP_COMPOSITOR;
display->grab_window = NULL;
display->grab_screen = screen;
display->grab_have_pointer = TRUE;
display->grab_have_keyboard = TRUE;
compositor->modal_plugin = plugin;
return TRUE;
fail:
@ -477,6 +502,79 @@ meta_begin_modal_for_plugin (MetaScreen *screen,
return FALSE;
}
static gboolean
begin_modal_wayland (MetaScreen *screen,
MetaPlugin *plugin,
MetaModalOptions options,
guint32 timestamp)
{
MetaWaylandCompositor *compositor;
gboolean pointer_grabbed = FALSE;
gboolean keyboard_grabbed = FALSE;
compositor = meta_wayland_compositor_get_default ();
if ((options & META_MODAL_POINTER_ALREADY_GRABBED) == 0)
{
if (!meta_wayland_pointer_begin_modal (&compositor->seat->pointer))
goto fail;
pointer_grabbed = TRUE;
}
if ((options & META_MODAL_KEYBOARD_ALREADY_GRABBED) == 0)
{
if (!meta_wayland_keyboard_begin_modal (&compositor->seat->keyboard))
goto fail;
keyboard_grabbed = TRUE;
}
return TRUE;
fail:
if (pointer_grabbed)
meta_wayland_pointer_end_modal (&compositor->seat->pointer);
if (keyboard_grabbed)
meta_wayland_keyboard_end_modal (&compositor->seat->keyboard);
return FALSE;
}
gboolean
meta_begin_modal_for_plugin (MetaScreen *screen,
MetaPlugin *plugin,
MetaModalOptions options,
guint32 timestamp)
{
/* To some extent this duplicates code in meta_display_begin_grab_op(), but there
* are significant differences in how we handle grabs that make it difficult to
* merge the two.
*/
MetaDisplay *display = meta_screen_get_display (screen);
MetaCompositor *compositor = display->compositor;
gboolean ok;
if (compositor->modal_plugin != NULL || display->grab_op != META_GRAB_OP_NONE)
return FALSE;
if (meta_is_wayland_compositor ())
ok = begin_modal_wayland (screen, plugin, options, timestamp);
else
ok = begin_modal_x11 (screen, plugin, options, timestamp);
if (!ok)
return FALSE;
display->grab_op = META_GRAB_OP_COMPOSITOR;
display->grab_window = NULL;
display->grab_screen = screen;
display->grab_have_pointer = TRUE;
display->grab_have_keyboard = TRUE;
compositor->modal_plugin = plugin;
return TRUE;
}
void
meta_end_modal_for_plugin (MetaScreen *screen,
MetaPlugin *plugin,
@ -488,8 +586,18 @@ meta_end_modal_for_plugin (MetaScreen *screen,
g_return_if_fail (compositor->modal_plugin == plugin);
XIUngrabDevice (xdpy, META_VIRTUAL_CORE_POINTER_ID, timestamp);
XIUngrabDevice (xdpy, META_VIRTUAL_CORE_KEYBOARD_ID, timestamp);
if (meta_is_wayland_compositor ())
{
MetaWaylandCompositor *compositor = meta_wayland_compositor_get_default ();
meta_wayland_pointer_end_modal (&compositor->seat->pointer);
meta_wayland_keyboard_end_modal (&compositor->seat->keyboard);
}
else
{
XIUngrabDevice (xdpy, META_VIRTUAL_CORE_POINTER_ID, timestamp);
XIUngrabDevice (xdpy, META_VIRTUAL_CORE_KEYBOARD_ID, timestamp);
}
display->grab_op = META_GRAB_OP_NONE;
display->grab_window = NULL;
@ -514,20 +622,20 @@ meta_check_end_modal (MetaScreen *screen)
{
meta_end_modal_for_plugin (screen,
compositor->modal_plugin,
CurrentTime);
CurrentTime);
}
}
static gboolean
after_stage_paint (gpointer data)
static void
after_stage_paint (ClutterStage *stage,
gpointer data)
{
MetaCompScreen *info = (MetaCompScreen*) data;
GList *l;
for (l = info->windows; l; l = l->next)
meta_window_actor_post_paint (l->data);
return TRUE;
}
static void
@ -541,6 +649,11 @@ redirect_windows (MetaCompositor *compositor,
guint n_retries;
guint max_retries;
/* If we're running with wayland, connected to a headless xwayland
* server then all the windows are implicitly redirected offscreen
* already and it would generate an error to try and explicitly
* redirect them via XCompositeRedirectSubwindows() */
if (meta_get_replace_current_wm ())
max_retries = 5;
else
@ -581,8 +694,11 @@ meta_compositor_manage_screen (MetaCompositor *compositor,
MetaCompScreen *info;
MetaDisplay *display = meta_screen_get_display (screen);
Display *xdisplay = meta_display_get_xdisplay (display);
Window xwin;
Window xwin = None;
gint width, height;
#ifdef HAVE_WAYLAND
MetaWaylandCompositor *wayland_compositor;
#endif
/* Check if the screen is already managed */
if (meta_screen_get_compositor_data (screen))
@ -595,7 +711,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;
@ -606,106 +729,136 @@ 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... */
#ifdef HAVE_WAYLAND
if (meta_is_wayland_compositor ())
{
wayland_compositor = meta_wayland_compositor_get_default ();
info->stage = wayland_compositor->stage;
clutter_threads_add_repaint_func_full (CLUTTER_REPAINT_FLAGS_POST_PAINT,
after_stage_paint,
info, NULL);
meta_screen_get_size (screen, &width, &height);
clutter_actor_set_size (info->stage, width, height);
}
else
#endif /* HAVE_WAYLAND */
{
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,
info,
NULL);
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);
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);
info->overlay_group = clutter_actor_new ();
clutter_actor_add_child (info->stage, info->window_group);
clutter_actor_add_child (info->stage, info->top_window_group);
clutter_actor_add_child (info->stage, info->overlay_group);
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);
/* 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)
{
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);
}
clutter_actor_show (info->overlay_group);
/* 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);
clutter_actor_show (info->stage);
}
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);
}
}
/*
@ -777,15 +930,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);
@ -845,8 +1001,8 @@ is_grabbed_event (MetaDisplay *display,
}
void
meta_compositor_window_shape_changed (MetaCompositor *compositor,
MetaWindow *window)
meta_compositor_window_x11_shape_changed (MetaCompositor *compositor,
MetaWindow *window)
{
MetaWindowActor *window_actor;
window_actor = META_WINDOW_ACTOR (meta_window_get_compositor_private (window));
@ -972,7 +1128,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
@ -991,7 +1148,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
@ -1333,22 +1490,40 @@ meta_compositor_sync_screen_size (MetaCompositor *compositor,
guint width,
guint height)
{
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
{
MetaDisplay *display = meta_screen_get_display (screen);
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
@ -1412,29 +1587,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)
@ -1566,8 +1744,10 @@ void
meta_enable_unredirect_for_screen (MetaScreen *screen)
{
MetaCompScreen *info = meta_screen_get_compositor_data (screen);
if (info != NULL)
info->disable_unredirect_count = MAX(0, info->disable_unredirect_count - 1);
if (info != NULL && info->disable_unredirect_count == 0)
g_warning ("Called enable_unredirect_for_screen while unredirection is enabled.");
if (info != NULL && info->disable_unredirect_count > 0)
info->disable_unredirect_count = info->disable_unredirect_count - 1;
}
#define FLASH_TIME_MS 50

View File

@ -472,6 +472,17 @@ meta_background_dispose (GObject *object)
G_OBJECT_CLASS (meta_background_parent_class)->dispose (object);
}
static void
meta_background_finalize (GObject *object)
{
MetaBackground *self = META_BACKGROUND (object);
MetaBackgroundPrivate *priv = self->priv;
g_free (priv->filename);
G_OBJECT_CLASS (meta_background_parent_class)->finalize (object);
}
static void
ensure_pipeline (MetaBackground *self)
{
@ -643,6 +654,7 @@ meta_background_class_init (MetaBackgroundClass *klass)
g_type_class_add_private (klass, sizeof (MetaBackgroundPrivate));
object_class->dispose = meta_background_dispose;
object_class->finalize = meta_background_finalize;
object_class->set_property = meta_background_set_property;
object_class->get_property = meta_background_get_property;
@ -1048,7 +1060,7 @@ meta_background_load_file_finish (MetaBackground *self,
texture = cogl_texture_new_from_data (width,
height,
COGL_TEXTURE_NO_SLICING,
COGL_TEXTURE_NO_ATLAS,
has_alpha ?
COGL_PIXEL_FORMAT_RGBA_8888 :
COGL_PIXEL_FORMAT_RGB_888,

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

@ -123,12 +123,12 @@ static guint signals[LAST_SIGNAL] = { 0 };
/* The first element in this array also defines the default parameters
* for newly created classes */
MetaShadowClassInfo default_shadow_classes[] = {
{ "normal", { 6, -1, 0, 3, 255 }, { 3, -1, 0, 3, 128 } },
{ "dialog", { 6, -1, 0, 3, 255 }, { 3, -1, 0, 3, 128 } },
{ "modal_dialog", { 6, -1, 0, 1, 255 }, { 3, -1, 0, 3, 128 } },
{ "utility", { 3, -1, 0, 1, 255 }, { 3, -1, 0, 1, 128 } },
{ "border", { 6, -1, 0, 3, 255 }, { 3, -1, 0, 3, 128 } },
{ "menu", { 6, -1, 0, 3, 255 }, { 3, -1, 0, 0, 128 } },
{ "normal", { 6, -1, 0, 3, 128 }, { 3, -1, 0, 3, 32 } },
{ "dialog", { 6, -1, 0, 3, 128 }, { 3, -1, 0, 3, 32 } },
{ "modal_dialog", { 6, -1, 0, 1, 128 }, { 3, -1, 0, 3, 32 } },
{ "utility", { 3, -1, 0, 1, 128 }, { 3, -1, 0, 1, 32 } },
{ "border", { 6, -1, 0, 3, 128 }, { 3, -1, 0, 3, 32 } },
{ "menu", { 6, -1, 0, 3, 128 }, { 3, -1, 0, 0, 32 } },
{ "popup-menu", { 1, -1, 0, 1, 128 }, { 1, -1, 0, 1, 128 } },

View File

@ -30,8 +30,14 @@
#include <config.h>
#include <meta/meta-shaped-texture.h>
#include <meta/util.h>
#include "meta-texture-tower.h"
#ifdef HAVE_WAYLAND
#include "meta-wayland-private.h"
#include <cogl/cogl-wayland-server.h>
#endif
#include <clutter/clutter.h>
#include <cogl/cogl.h>
#include <cogl/cogl-texture-pixmap-x11.h>
@ -55,6 +61,15 @@ 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,
#ifdef HAVE_WAYLAND
META_SHAPED_TEXTURE_TYPE_WAYLAND_SURFACE,
#endif
} MetaShapedTextureType;
G_DEFINE_TYPE (MetaShapedTexture, meta_shaped_texture,
CLUTTER_TYPE_ACTOR);
@ -65,13 +80,27 @@ G_DEFINE_TYPE (MetaShapedTexture, meta_shaped_texture,
struct _MetaShapedTexturePrivate
{
MetaTextureTower *paint_tower;
Pixmap pixmap;
CoglTexturePixmapX11 *texture;
MetaShapedTextureType type;
union {
struct {
Pixmap pixmap;
} x11;
#ifdef HAVE_WAYLAND
struct {
MetaWaylandSurface *surface;
} wayland;
#endif
};
CoglTexture *texture;
CoglTexture *mask_texture;
CoglPipeline *pipeline;
CoglPipeline *pipeline_unshaped;
cairo_region_t *clip_region;
cairo_region_t *input_shape_region;
guint tex_width, tex_height;
@ -103,7 +132,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;
}
@ -128,6 +160,56 @@ meta_shaped_texture_dispose (GObject *object)
G_OBJECT_CLASS (meta_shaped_texture_parent_class)->dispose (object);
}
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 (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 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)
{
@ -285,38 +367,53 @@ 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)
if (priv->input_shape_region == NULL)
CLUTTER_ACTOR_CLASS (meta_shaped_texture_parent_class)
->pick (actor, color);
else if (clutter_actor_should_pick_paint (actor))
else
{
CoglTexture *paint_tex;
ClutterActorBox alloc;
guint tex_width, tex_height;
int n_rects;
float *rectangles;
int i;
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);
cogl_set_source_color4ub (color->red,
color->green,
color->blue,
color->alpha);
/* 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_rectangles (rectangles, n_rects);
}
}
@ -365,12 +462,48 @@ meta_shaped_texture_get_paint_volume (ClutterActor *self,
return clutter_paint_volume_set_from_allocation (volume, self);
}
#ifdef HAVE_WAYLAND
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;
}
#endif /* HAVE_WAYLAND */
ClutterActor *
meta_shaped_texture_new_with_xwindow (Window xwindow)
{
return g_object_new (META_TYPE_SHAPED_TEXTURE, NULL);
}
void
@ -389,8 +522,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 +548,146 @@ 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)
#ifdef HAVE_WAYLAND
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));
}
}
}
#endif /* HAVE_WAYLAND */
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;
#ifdef HAVE_WAYLAND
case META_SHAPED_TEXTURE_TYPE_WAYLAND_SURFACE:
wayland_surface_update_area (stex, x, y, width, height);
break;
#endif
}
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 +705,18 @@ 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);
@ -520,6 +726,54 @@ meta_shaped_texture_set_pixmap (MetaShapedTexture *stex,
COGL_TEXTURE (priv->texture));
}
#ifdef HAVE_WAYLAND
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);
if (priv->create_mipmaps)
meta_texture_tower_set_base_texture (priv->paint_tower,
COGL_TEXTURE (priv->texture));
}
#endif /* HAVE_WAYLAND */
/**
* meta_shaped_texture_get_texture:
* @stex: The #MetaShapedTexture
@ -533,6 +787,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

View File

@ -5,6 +5,11 @@
#include <config.h>
#ifdef HAVE_WAYLAND
#include <wayland-server.h>
#include <meta-wayland-private.h>
#endif
#include <X11/extensions/Xdamage.h>
#include <meta/compositor-mutter.h>
@ -24,8 +29,20 @@ 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);
#ifdef HAVE_WAYLAND
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);
#endif
void meta_window_actor_pre_paint (MetaWindowActor *self);
void meta_window_actor_post_paint (MetaWindowActor *self);

File diff suppressed because it is too large Load Diff

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
{
@ -91,14 +92,15 @@ meta_window_group_paint (ClutterActor *actor)
{
cairo_region_t *visible_region;
ClutterActor *stage;
ClutterActorIter iter;
ClutterActor *child;
cairo_rectangle_int_t visible_rect;
GList *children, *l;
int paint_x_origin, paint_y_origin;
int actor_x_origin, actor_y_origin;
int paint_x_offset, paint_y_offset;
MetaWindowGroup *window_group = META_WINDOW_GROUP (actor);
MetaCompScreen *info = meta_screen_get_compositor_data (window_group->screen);
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
@ -123,13 +125,6 @@ meta_window_group_paint (ClutterActor *actor)
paint_x_offset = paint_x_origin - actor_x_origin;
paint_y_offset = paint_y_origin - actor_y_origin;
/* We walk the list from top to bottom (opposite of painting order),
* and subtract the opaque area of each window out of the visible
* region that we pass to the windows below.
*/
children = clutter_actor_get_children (actor);
children = g_list_reverse (children);
/* Get the clipped redraw bounds from Clutter so that we can avoid
* painting shadows on windows that don't need to be painted in this
* frame. In the case of a multihead setup with mismatched monitor
@ -142,21 +137,32 @@ 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);
}
}
for (l = children; l; l = l->next)
/* We walk the list from top to bottom (opposite of painting order),
* and subtract the opaque area of each window out of the visible
* region that we pass to the windows below.
*/
clutter_actor_iter_init (&iter, actor);
while (clutter_actor_iter_prev (&iter, &child))
{
if (!CLUTTER_ACTOR_IS_VISIBLE (l->data))
if (!CLUTTER_ACTOR_IS_VISIBLE (child))
continue;
if (l->data == info->unredirected_window)
if (!meta_is_wayland_compositor () &&
info->unredirected_window != NULL &&
child == CLUTTER_ACTOR (info->unredirected_window))
continue;
/* If an actor has effects applied, then that can change the area
@ -175,12 +181,13 @@ meta_window_group_paint (ClutterActor *actor)
* as well for the same reason, but omitted for simplicity in the
* hopes that no-one will do that.
*/
if (clutter_actor_has_effects (l->data))
if (clutter_actor_has_effects (child))
continue;
if (META_IS_WINDOW_ACTOR (l->data))
if (META_IS_WINDOW_ACTOR (child))
{
MetaWindowActor *window_actor = l->data;
MetaWindow *meta_window;
MetaWindowActor *window_actor = child;
int x, y;
if (!meta_actor_is_untransformed (CLUTTER_ACTOR (window_actor), &x, &y))
@ -194,7 +201,14 @@ meta_window_group_paint (ClutterActor *actor)
meta_window_actor_set_visible_region (window_actor, visible_region);
if (clutter_actor_get_paint_opacity (CLUTTER_ACTOR (window_actor)) == 0xff)
/* TODO: Track the opaque regions of wayland clients.
* Although wayland clients can report opaque window
* regions, for now we assume that all wayland clients are
* transparent... */
meta_window = meta_window_actor_get_meta_window (window_actor);
if (meta_window->client_type != META_WINDOW_CLIENT_TYPE_WAYLAND &&
clutter_actor_get_paint_opacity (CLUTTER_ACTOR (window_actor)) == 0xff)
{
cairo_region_t *obscured_region = meta_window_actor_get_obscured_region (window_actor);
if (obscured_region)
@ -204,13 +218,12 @@ meta_window_group_paint (ClutterActor *actor)
meta_window_actor_set_visible_region_beneath (window_actor, visible_region);
cairo_region_translate (visible_region, x, y);
}
else if (META_IS_BACKGROUND_ACTOR (l->data) ||
META_IS_BACKGROUND_GROUP (l->data))
else if (META_IS_BACKGROUND_ACTOR (child) ||
META_IS_BACKGROUND_GROUP (child))
{
ClutterActor *background_actor = l->data;
int x, y;
if (!meta_actor_is_untransformed (CLUTTER_ACTOR (background_actor), &x, &y))
if (!meta_actor_is_untransformed (child, &x, &y))
continue;
x += paint_x_offset;
@ -218,10 +231,10 @@ meta_window_group_paint (ClutterActor *actor)
cairo_region_translate (visible_region, - x, - y);
if (META_IS_BACKGROUND_GROUP (background_actor))
meta_background_group_set_visible_region (META_BACKGROUND_GROUP (background_actor), visible_region);
if (META_IS_BACKGROUND_GROUP (child))
meta_background_group_set_visible_region (META_BACKGROUND_GROUP (child), visible_region);
else
meta_background_actor_set_visible_region (META_BACKGROUND_ACTOR (background_actor), visible_region);
meta_background_actor_set_visible_region (META_BACKGROUND_ACTOR (child), visible_region);
cairo_region_translate (visible_region, x, y);
}
}
@ -233,21 +246,20 @@ meta_window_group_paint (ClutterActor *actor)
/* Now that we are done painting, unset the visible regions (they will
* mess up painting clones of our actors)
*/
for (l = children; l; l = l->next)
clutter_actor_iter_init (&iter, actor);
while (clutter_actor_iter_next (&iter, &child))
{
if (META_IS_WINDOW_ACTOR (l->data))
if (META_IS_WINDOW_ACTOR (child))
{
MetaWindowActor *window_actor = l->data;
MetaWindowActor *window_actor = META_WINDOW_ACTOR (child);
meta_window_actor_reset_visible_regions (window_actor);
}
else if (META_IS_BACKGROUND_ACTOR (l->data))
else if (META_IS_BACKGROUND_ACTOR (child))
{
MetaBackgroundActor *background_actor = l->data;
MetaBackgroundActor *background_actor = META_BACKGROUND_ACTOR (child);
meta_background_actor_set_visible_region (background_actor, NULL);
}
}
g_list_free (children);
}
static gboolean

View File

@ -24,7 +24,10 @@
#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 <stdlib.h>
#include <libintl.h>
#define _(x) dgettext (GETTEXT_PACKAGE, x)
#define N_(x) x
@ -98,6 +101,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 +118,8 @@ struct _MetaDefaultPluginPrivate
ClutterActor *desktop1;
ClutterActor *desktop2;
ClutterActor *background_group;
MetaPluginInfo info;
};
@ -203,6 +210,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 +307,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 +839,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

@ -59,6 +59,8 @@ struct _MetaBarrierPrivate
PointerBarrier xbarrier;
};
static void meta_barrier_event_unref (MetaBarrierEvent *event);
static void
meta_barrier_get_property (GObject *object,
guint prop_id,
@ -359,6 +361,8 @@ meta_barrier_fire_event (MetaBarrier *barrier,
default:
g_assert_not_reached ();
}
meta_barrier_event_unref (event);
}
gboolean

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;
@ -103,19 +111,21 @@ struct _MetaDisplay
#include <meta/atomnames.h>
#undef item
/* This is the actual window from focus events,
* not the one we last set
/* The window and serial of the most recent FocusIn event. */
Window server_focus_window;
gulong server_focus_serial;
/* Our best guess as to the "currently" focused window (that is, the
* window that we expect will be focused at the point when the X
* server processes our next request), and the serial of the request
* or event that caused this.
*/
MetaWindow *focus_window;
/* window we are expecting a FocusIn event for or the current focus
* window if we are not expecting any FocusIn/FocusOut events; not
* perfect because applications can call XSetInputFocus directly.
* (It could also be messed up if a timestamp later than current
* time is sent to meta_display_set_input_focus_window, though that
* would be a programming error). See bug 154598 for more info.
*/
MetaWindow *expected_focus_window;
/* For windows we've focused that don't necessarily have an X window,
* 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;
@ -239,6 +249,8 @@ struct _MetaDisplay
unsigned int meta_mask;
MetaKeyCombo overlay_key_combo;
gboolean overlay_key_only_pressed;
MetaKeyCombo *iso_next_group_combos;
int n_iso_next_group_combos;
/* Monitor cache */
unsigned int monitor_cache_invalidated : 1;
@ -457,14 +469,25 @@ void meta_display_remove_autoraise_callback (MetaDisplay *display);
void meta_display_overlay_key_activate (MetaDisplay *display);
void meta_display_accelerator_activate (MetaDisplay *display,
guint action,
guint deviceid);
guint deviceid,
guint timestamp);
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,
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>
@ -139,6 +140,7 @@ enum
{
OVERLAY_KEY,
ACCELERATOR_ACTIVATED,
MODIFIERS_ACCELERATOR_ACTIVATED,
FOCUS_WINDOW,
WINDOW_CREATED,
WINDOW_DEMANDS_ATTENTION,
@ -253,7 +255,26 @@ meta_display_class_init (MetaDisplayClass *klass)
G_SIGNAL_RUN_LAST,
0,
NULL, NULL, NULL,
G_TYPE_NONE, 2, G_TYPE_UINT, G_TYPE_UINT);
G_TYPE_NONE, 3, G_TYPE_UINT, G_TYPE_UINT, G_TYPE_UINT);
/**
* MetaDisplay::modifiers-accelerator-activated:
* @display: the #MetaDisplay instance
*
* The ::modifiers-accelerator-activated signal will be emitted when
* a special modifiers-only keybinding is activated.
*
* Returns: %TRUE means that the keyboard device should remain
* frozen and %FALSE for the default behavior of unfreezing the
* keyboard.
*/
display_signals[MODIFIERS_ACCELERATOR_ACTIVATED] =
g_signal_new ("modifiers-accelerator-activated",
G_TYPE_FROM_CLASS (klass),
G_SIGNAL_RUN_LAST,
0,
g_signal_accumulator_first_wins, NULL, NULL,
G_TYPE_BOOLEAN, 0);
display_signals[WINDOW_CREATED] =
g_signal_new ("window-created",
@ -497,7 +518,7 @@ meta_display_open (void)
if (meta_is_syncing ())
XSynchronize (xdisplay, True);
g_assert (the_display == NULL);
the_display = g_object_new (META_TYPE_DISPLAY, NULL);
@ -519,7 +540,9 @@ meta_display_open (void)
the_display->autoraise_timeout_id = 0;
the_display->autoraise_window = NULL;
the_display->focus_window = NULL;
the_display->expected_focus_window = NULL;
the_display->focus_serial = 0;
the_display->server_focus_window = None;
the_display->server_focus_serial = 0;
the_display->grab_old_window_stacking = NULL;
the_display->mouse_mode = TRUE; /* Only relevant for mouse or sloppy focus */
@ -909,8 +932,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;
}
@ -1445,6 +1484,17 @@ meta_display_get_current_time (MetaDisplay *display)
return display->current_time;
}
static Bool
find_timestamp_predicate (Display *xdisplay,
XEvent *ev,
XPointer arg)
{
MetaDisplay *display = (MetaDisplay *) arg;
return (ev->type == PropertyNotify &&
ev->xproperty.atom == display->atom__MUTTER_TIMESTAMP_PING);
}
/* Get a timestamp, even if it means a roundtrip */
guint32
meta_display_get_current_time_roundtrip (MetaDisplay *display)
@ -1456,17 +1506,13 @@ meta_display_get_current_time_roundtrip (MetaDisplay *display)
{
XEvent property_event;
/* Using the property XA_PRIMARY because it's safe; nothing
* would use it as a property. The type doesn't matter.
*/
XChangeProperty (display->xdisplay,
display->timestamp_pinging_window,
XA_PRIMARY, XA_STRING, 8,
PropModeAppend, NULL, 0);
XWindowEvent (display->xdisplay,
display->timestamp_pinging_window,
PropertyChangeMask,
&property_event);
XChangeProperty (display->xdisplay, display->timestamp_pinging_window,
display->atom__MUTTER_TIMESTAMP_PING,
XA_STRING, 8, PropModeAppend, NULL, 0);
XIfEvent (display->xdisplay,
&property_event,
find_timestamp_predicate,
(XPointer) display);
timestamp = property_event.xproperty.time;
}
@ -1632,12 +1678,12 @@ meta_display_mouse_mode_focus (MetaDisplay *display,
* alternative mechanism works great.
*/
if (meta_prefs_get_focus_mode() == G_DESKTOP_FOCUS_MODE_MOUSE &&
display->expected_focus_window != NULL)
display->focus_window != NULL)
{
meta_topic (META_DEBUG_FOCUS,
"Unsetting focus from %s due to mouse entering "
"the DESKTOP window\n",
display->expected_focus_window->desc);
display->focus_window->desc);
meta_display_focus_the_no_focus_window (display,
window->screen,
timestamp);
@ -1851,11 +1897,289 @@ get_input_event (MetaDisplay *display,
return NULL;
}
static void
update_focus_window (MetaDisplay *display,
MetaFocusType type,
MetaWindow *window,
Window xwindow,
gulong serial)
{
#ifdef HAVE_WAYLAND
MetaWaylandCompositor *compositor;
#endif
display->focus_serial = serial;
if (display->focus_xwindow == xwindow &&
display->focus_type == type &&
display->focus_window == window)
return;
if (display->focus_window)
{
MetaWindow *previous;
meta_topic (META_DEBUG_FOCUS,
"%s is now the previous focus window due to being focused out or unmapped\n",
display->focus_window->desc);
/* Make sure that signals handlers invoked by
* meta_window_set_focused_internal() don't see
* display->focus_window->has_focus == FALSE
*/
previous = display->focus_window;
display->focus_window = NULL;
display->focus_xwindow = None;
meta_window_set_focused_internal (previous, FALSE);
}
display->focus_type = type;
display->focus_window = window;
display->focus_xwindow = xwindow;
if (display->focus_window)
{
meta_topic (META_DEBUG_FOCUS, "* Focus --> %s with serial %lu\n",
display->focus_window->desc, serial);
meta_window_set_focused_internal (display->focus_window, TRUE);
}
else
meta_topic (META_DEBUG_FOCUS, "* Focus --> NULL with serial %lu\n", serial);
#ifdef HAVE_WAYLAND
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");
}
#endif
g_object_notify (G_OBJECT (display), "focus-window");
meta_display_update_active_window_hint (display);
}
static gboolean
timestamp_too_old (MetaDisplay *display,
guint32 *timestamp)
{
/* FIXME: If Soeren's suggestion in bug 151984 is implemented, it will allow
* us to sanity check the timestamp here and ensure it doesn't correspond to
* a future time (though we would want to rename to
* timestamp_too_old_or_in_future).
*/
if (*timestamp == CurrentTime)
{
*timestamp = meta_display_get_current_time_roundtrip (display);
return FALSE;
}
else if (XSERVER_TIME_IS_BEFORE (*timestamp, display->last_focus_time))
{
if (XSERVER_TIME_IS_BEFORE (*timestamp, display->last_user_time))
return TRUE;
else
{
*timestamp = display->last_focus_time;
return FALSE;
}
}
return FALSE;
}
static void
request_xserver_input_focus_change (MetaDisplay *display,
MetaScreen *screen,
MetaFocusType type,
MetaWindow *meta_window,
Window xwindow,
guint32 timestamp)
{
gulong serial;
if (timestamp_too_old (display, &timestamp))
return;
meta_error_trap_push (display);
/* In order for mutter to know that the focus request succeeded, we track
* the serial of the "focus request" we made, but if we take the serial
* of the XSetInputFocus request, then there's no way to determine the
* difference between focus events as a result of the SetInputFocus and
* focus events that other clients send around the same time. Ensure that
* we know which is which by making two requests that the server will
* process at the same time.
*/
meta_display_grab (display);
serial = XNextRequest (display->xdisplay);
XSetInputFocus (display->xdisplay,
xwindow,
RevertToPointerRoot,
timestamp);
XChangeProperty (display->xdisplay, display->timestamp_pinging_window,
display->atom__MUTTER_FOCUS_SET,
XA_STRING, 8, PropModeAppend, NULL, 0);
meta_display_ungrab (display);
update_focus_window (display,
type,
meta_window,
xwindow,
serial);
meta_error_trap_pop (display);
display->last_focus_time = timestamp;
display->active_screen = screen;
if (meta_window == NULL || meta_window != display->autoraise_window)
meta_display_remove_autoraise_callback (display);
}
static void
handle_window_focus_event (MetaDisplay *display,
MetaWindow *window,
XIEnterEvent *event,
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
*/
if (window)
{
if (event->event == window->xwindow)
window_type = "client window";
else if (window->frame && event->event == window->frame->xwindow)
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";
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",
event->evtype == XI_FocusIn ? "in" :
event->evtype == XI_FocusOut ? "out" :
"???",
window ? window->desc : "",
event->event, window_type,
meta_event_mode_to_string (event->mode),
meta_event_detail_to_string (event->mode),
event->serial);
#endif
/* FIXME our pointer tracking is broken; see how
* gtk+/gdk/x11/gdkevents-x11.c or XFree86/xc/programs/xterm/misc.c
* for how to handle it the correct way. In brief you need to track
* pointer focus and regular focus, and handle EnterNotify in
* PointerRoot mode with no window manager. However as noted above,
* accurate focus tracking will break things because we want to keep
* windows "focused" when using keybindings on them, and also we
* sometimes "focus" a window by focusing its frame or
* no_focus_window; so this all needs rethinking massively.
*
* My suggestion is to change it so that we clearly separate
* actual keyboard focus tracking using the xterm algorithm,
* and mutter's "pretend" focus window, and go through all
* the code and decide which one should be used in each place;
* a hard bit is deciding on a policy for that.
*
* http://bugzilla.gnome.org/show_bug.cgi?id=90382
*/
/* We ignore grabs, though this is questionable. It may be better to
* increase the intelligence of the focus window tracking.
*
* The problem is that keybindings for windows are done with
* XGrabKey, which means focus_window disappears and the front of
* the MRU list gets confused from what the user expects once a
* keybinding is used.
*/
if (event->mode == XINotifyGrab ||
event->mode == XINotifyUngrab ||
/* From WindowMaker, ignore all funky pointer root events */
event->detail > XINotifyNonlinearVirtual)
{
meta_topic (META_DEBUG_FOCUS,
"Ignoring focus event generated by a grab or other weirdness\n");
return;
}
if (event->evtype == XI_FocusIn)
{
display->server_focus_window = event->event;
display->server_focus_serial = serial;
focus_window = window;
}
else if (event->evtype == XI_FocusOut)
{
if (event->detail == XINotifyInferior)
{
/* This event means the client moved focus to a subwindow */
meta_topic (META_DEBUG_FOCUS,
"Ignoring focus out with NotifyInferior\n");
return;
}
display->server_focus_window = None;
display->server_focus_serial = serial;
focus_window = NULL;
}
else
g_return_if_reached ();
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);
}
}
/**
* 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.
@ -1865,21 +2189,20 @@ get_input_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);
@ -1888,12 +2211,40 @@ 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;
display->current_time = event_get_time (display, event);
display->monitor_cache_invalidated = TRUE;
if (event->xany.serial > display->focus_serial &&
display->focus_window &&
display->focus_window->xwindow != display->server_focus_window)
{
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);
@ -1966,6 +2317,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 */
}
meta_idle_monitor_handle_xevent_all (event);
}
#endif /* HAVE_XSYNC */
@ -2003,8 +2356,35 @@ event_callback (XEvent *event,
}
if (display->compositor)
meta_compositor_window_shape_changed (display->compositor,
window);
meta_compositor_window_x11_shape_changed (display->compositor,
window);
}
else if (sev->kind == ShapeInput)
{
if (sev->shaped && !window->has_input_shape)
{
window->has_input_shape = TRUE;
meta_topic (META_DEBUG_SHAPES,
"Window %s now has an input shape\n",
window->desc);
}
else if (!sev->shaped && window->has_input_shape)
{
window->has_input_shape = FALSE;
meta_topic (META_DEBUG_SHAPES,
"Window %s no longer has an input shape\n",
window->desc);
}
else
{
meta_topic (META_DEBUG_SHAPES,
"Window %s input shape changed\n",
window->desc);
}
if (display->compositor)
meta_compositor_window_x11_shape_changed (display->compositor,
window);
}
}
else
@ -2213,30 +2593,12 @@ event_callback (XEvent *event,
/* This is from our synchronous grab since
* it has no modifiers and was on the client window
*/
int mode;
/* When clicking a different app in click-to-focus
* in application-based mode, and the different
* app is not a dock or desktop, eat the focus click.
*/
if (meta_prefs_get_focus_mode () == G_DESKTOP_FOCUS_MODE_CLICK &&
meta_prefs_get_application_based () &&
!window->has_focus &&
window->type != META_WINDOW_DOCK &&
window->type != META_WINDOW_DESKTOP &&
(display->focus_window == NULL ||
!meta_window_same_application (window,
display->focus_window)))
mode = XIAsyncDevice; /* eat focus click */
else
mode = XIReplayDevice; /* give event back */
meta_verbose ("Allowing events mode %s time %u\n",
mode == AsyncPointer ? "AsyncPointer" : "ReplayPointer",
meta_verbose ("Allowing events time %u\n",
(unsigned int)device_event->time);
XIAllowEvents (display->xdisplay, device_event->deviceid,
mode, device_event->time);
XIReplayDevice, device_event->time);
}
if (begin_move && window->has_move_func)
@ -2350,40 +2712,19 @@ event_callback (XEvent *event,
break;
case XI_FocusIn:
case XI_FocusOut:
if (window)
{
meta_window_notify_focus (window, enter_event);
}
else if (meta_display_xwindow_is_a_no_focus_window (display,
enter_event->event))
{
meta_topic (META_DEBUG_FOCUS,
"Focus %s event received on no_focus_window 0x%lx "
"mode %s detail %s\n",
enter_event->evtype == XI_FocusIn ? "in" :
enter_event->evtype == XI_FocusOut ? "out" :
"???",
enter_event->event,
meta_event_mode_to_string (enter_event->mode),
meta_event_detail_to_string (enter_event->detail));
}
else
/* libXi does not properly copy the serial to the XIEnterEvent, so pull it
* from the parent XAnyEvent.
* See: https://bugs.freedesktop.org/show_bug.cgi?id=64687
*/
handle_window_focus_event (display, window, enter_event, event->xany.serial);
if (!window)
{
/* Check if the window is a root window. */
MetaScreen *screen =
meta_display_screen_for_root(display,
enter_event->event);
if (screen == NULL)
break;
meta_topic (META_DEBUG_FOCUS,
"Focus %s event received on root window 0x%lx "
"mode %s detail %s\n",
enter_event->evtype == XI_FocusIn ? "in" :
enter_event->evtype == XI_FocusOut ? "out" :
"???",
enter_event->event,
meta_event_mode_to_string (enter_event->mode),
meta_event_detail_to_string (enter_event->detail));
if (enter_event->evtype == XI_FocusIn &&
enter_event->mode == XINotifyDetailNone)
@ -2521,13 +2862,6 @@ event_callback (XEvent *event,
window->unmaps_pending);
}
}
/* Unfocus on UnmapNotify, do this after the possible
* window_free above so that window_free can see if window->has_focus
* and move focus to another window
*/
if (window)
meta_window_lost_focus (window);
}
break;
case MapNotify:
@ -2587,32 +2921,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 */
@ -2807,27 +3119,6 @@ event_callback (XEvent *event,
meta_workspace_focus_default_window (screen->active_workspace, NULL, timestamp);
}
}
else if (event->xclient.message_type ==
display->atom__MUTTER_RELOAD_THEME_MESSAGE)
{
meta_verbose ("Received reload theme request\n");
meta_ui_set_current_theme (meta_prefs_get_theme (),
TRUE);
meta_display_retheme_all ();
}
else if (event->xclient.message_type ==
display->atom__MUTTER_SET_KEYBINDINGS_MESSAGE)
{
meta_verbose ("Received set keybindings request = %d\n",
(int) event->xclient.data.l[0]);
meta_set_keybindings_disabled (!event->xclient.data.l[0]);
}
else if (event->xclient.message_type ==
display->atom__MUTTER_TOGGLE_VERBOSE)
{
meta_verbose ("Received toggle verbose message\n");
meta_set_verbose (!meta_is_verbose ());
}
else if (event->xclient.message_type ==
display->atom_WM_PROTOCOLS)
{
@ -2924,6 +3215,32 @@ 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 */
#ifdef HAVE_WAYLAND
if (meta_is_wayland_compositor () &&
event->type == GenericEvent &&
event->xcookie.evtype == XI_Motion)
return FALSE;
#endif
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
@ -3537,6 +3854,12 @@ meta_spew_event (MetaDisplay *display,
if (event->type == (display->damage_event_base + XDamageNotify))
return;
if (event->type == (display->xsync_event_base + XSyncAlarmNotify))
return;
if (event->type == PropertyNotify && event->xproperty.atom == display->atom__NET_WM_USER_TIME)
return;
input_event = get_input_event (display, event);
if (input_event)
@ -5561,98 +5884,82 @@ sanity_check_timestamps (MetaDisplay *display,
}
}
static gboolean
timestamp_too_old (MetaDisplay *display,
MetaWindow *window,
guint32 *timestamp)
{
/* FIXME: If Soeren's suggestion in bug 151984 is implemented, it will allow
* us to sanity check the timestamp here and ensure it doesn't correspond to
* a future time (though we would want to rename to
* timestamp_too_old_or_in_future).
*/
if (*timestamp == CurrentTime)
{
meta_warning ("Got a request to focus %s with a timestamp of 0. This "
"shouldn't happen!\n",
window ? window->desc : "the no_focus_window");
*timestamp = meta_display_get_current_time_roundtrip (display);
return FALSE;
}
else if (XSERVER_TIME_IS_BEFORE (*timestamp, display->last_focus_time))
{
if (XSERVER_TIME_IS_BEFORE (*timestamp, display->last_user_time))
{
meta_topic (META_DEBUG_FOCUS,
"Ignoring focus request for %s since %u "
"is less than %u and %u.\n",
window ? window->desc : "the no_focus_window",
*timestamp,
display->last_user_time,
display->last_focus_time);
return TRUE;
}
else
{
meta_topic (META_DEBUG_FOCUS,
"Received focus request for %s which is newer than most "
"recent user_time, but less recent than "
"last_focus_time (%u < %u < %u); adjusting "
"accordingly. (See bug 167358)\n",
window ? window->desc : "the no_focus_window",
display->last_user_time,
*timestamp,
display->last_focus_time);
*timestamp = display->last_focus_time;
return FALSE;
}
}
return FALSE;
}
void
meta_display_set_input_focus_window (MetaDisplay *display,
MetaWindow *window,
gboolean focus_frame,
guint32 timestamp)
{
if (timestamp_too_old (display, window, &timestamp))
return;
meta_error_trap_push (display);
XSetInputFocus (display->xdisplay,
focus_frame ? window->frame->xwindow : window->xwindow,
RevertToPointerRoot,
timestamp);
meta_error_trap_pop (display);
display->expected_focus_window = window;
display->last_focus_time = timestamp;
display->active_screen = window->screen;
if (window != display->autoraise_window)
meta_display_remove_autoraise_callback (window->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);
}
void
meta_display_focus_the_no_focus_window (MetaDisplay *display,
meta_display_request_take_focus (MetaDisplay *display,
MetaWindow *window,
guint32 timestamp)
{
if (timestamp_too_old (display, &timestamp))
return;
meta_topic (META_DEBUG_FOCUS, "WM_TAKE_FOCUS(%s, %u)\n",
window->desc, timestamp);
if (window != display->focus_window)
{
/* The "Globally Active Input" window case, where the window
* doesn't want us to call XSetInputFocus on it, but does
* want us to send a WM_TAKE_FOCUS.
*
* We can't just set display->focus_window to @window, since we
* we don't know when (or even if) the window will actually take
* focus, so we could end up being wrong for arbitrarily long.
* But we also can't leave it set to the current window, or else
* bug #597352 would come back. So we focus the no_focus_window
* now (and set display->focus_window to that), send the
* WM_TAKE_FOCUS, and then just forget about @window
* until/unless we get a FocusIn.
*/
meta_display_focus_the_no_focus_window (display,
window->screen,
timestamp);
}
meta_window_send_icccm_message (window,
display->atom_WM_TAKE_FOCUS,
timestamp);
}
void
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);
}
void
meta_display_focus_the_no_focus_window (MetaDisplay *display,
MetaScreen *screen,
guint32 timestamp)
{
if (timestamp_too_old (display, NULL, &timestamp))
return;
XSetInputFocus (display->xdisplay,
screen->no_focus_window,
RevertToPointerRoot,
timestamp);
display->expected_focus_window = NULL;
display->last_focus_time = timestamp;
display->active_screen = screen;
meta_display_remove_autoraise_callback (display);
request_xserver_input_focus_change (display,
screen,
META_FOCUS_NO_FOCUS_WINDOW,
NULL,
screen->no_focus_window,
timestamp);
}
void
@ -5675,10 +5982,21 @@ meta_display_overlay_key_activate (MetaDisplay *display)
void
meta_display_accelerator_activate (MetaDisplay *display,
guint action,
guint deviceid)
guint deviceid,
guint timestamp)
{
g_signal_emit (display, display_signals[ACCELERATOR_ACTIVATED],
0, action, deviceid);
0, action, deviceid, timestamp);
}
gboolean
meta_display_modifiers_accelerator_activate (MetaDisplay *display)
{
gboolean freeze;
g_signal_emit (display, display_signals[MODIFIERS_ACCELERATOR_ACTIVATED], 0, &freeze);
return freeze;
}
void
@ -5762,11 +6080,9 @@ meta_display_has_shape (MetaDisplay *display)
* meta_display_get_focus_window:
* @display: a #MetaDisplay
*
* Get the window that, according to events received from X server,
* currently has the input focus. We may have already sent a request
* to the X server to move the focus window elsewhere. (The
* expected_focus_window records where we've last set the input
* focus.)
* Get our best guess as to the "currently" focused window (that is,
* the window that we expect will be focused at the point when the X
* server processes our next request).
*
* Return Value: (transfer none): The current focus window
*/

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,
@ -332,19 +339,6 @@ meta_frame_calc_borders (MetaFrame *frame,
borders);
}
void
meta_frame_get_corner_radiuses (MetaFrame *frame,
float *top_left,
float *top_right,
float *bottom_left,
float *bottom_right)
{
meta_ui_get_corner_radiuses (frame->window->screen->ui,
frame->xwindow,
top_left, top_right,
bottom_left, bottom_right);
}
gboolean
meta_frame_sync_to_window (MetaFrame *frame,
int resize_gravity,
@ -400,6 +394,14 @@ meta_frame_get_frame_bounds (MetaFrame *frame)
frame->rect.height);
}
void
meta_frame_get_mask (MetaFrame *frame,
cairo_t *cr)
{
meta_ui_get_frame_mask (frame->window->screen->ui, frame->xwindow,
frame->rect.width, frame->rect.height, cr);
}
void
meta_frame_queue_draw (MetaFrame *frame)
{

View File

@ -63,12 +63,6 @@ Window meta_frame_get_xwindow (MetaFrame *frame);
void meta_frame_calc_borders (MetaFrame *frame,
MetaFrameBorders *borders);
void meta_frame_get_corner_radiuses (MetaFrame *frame,
float *top_left,
float *top_right,
float *bottom_left,
float *bottom_right);
gboolean meta_frame_sync_to_window (MetaFrame *frame,
int gravity,
gboolean need_move,
@ -76,6 +70,9 @@ gboolean meta_frame_sync_to_window (MetaFrame *frame,
cairo_region_t *meta_frame_get_frame_bounds (MetaFrame *frame);
void meta_frame_get_mask (MetaFrame *frame,
cairo_t *cr);
void meta_frame_set_screen_cursor (MetaFrame *frame,
MetaCursor cursor);

View File

@ -69,7 +69,6 @@ void meta_window_ungrab_all_keys (MetaWindow *window,
gboolean meta_display_process_key_event (MetaDisplay *display,
MetaWindow *window,
XIDeviceEvent *event);
void meta_set_keybindings_disabled (gboolean setting);
void meta_display_process_mapping_event (MetaDisplay *display,
XEvent *event);

View File

@ -53,11 +53,13 @@
#include <X11/XKBlib.h>
#endif
#ifdef HAVE_WAYLAND
#include "meta-wayland-private.h"
#endif
#define SCHEMA_COMMON_KEYBINDINGS "org.gnome.desktop.wm.keybindings"
#define SCHEMA_MUTTER_KEYBINDINGS "org.gnome.mutter.keybindings"
static gboolean all_bindings_disabled = FALSE;
static gboolean add_builtin_keybinding (MetaDisplay *display,
const char *name,
GSettings *settings,
@ -302,6 +304,172 @@ reload_modmap (MetaDisplay *display)
display->meta_mask);
}
/* Original code from gdk_x11_keymap_get_entries_for_keyval() in
* gdkkeys-x11.c */
static int
get_keycodes_for_keysym (MetaDisplay *display,
int keysym,
int **keycodes)
{
GArray *retval;
int n_keycodes;
int keycode;
retval = g_array_new (FALSE, FALSE, sizeof (int));
keycode = display->min_keycode;
while (keycode <= display->max_keycode)
{
const KeySym *syms = display->keymap + (keycode - display->min_keycode) * display->keysyms_per_keycode;
int i = 0;
while (i < display->keysyms_per_keycode)
{
if (syms[i] == (unsigned int)keysym)
g_array_append_val (retval, keycode);
++i;
}
++keycode;
}
n_keycodes = retval->len;
*keycodes = (int*) g_array_free (retval, n_keycodes == 0 ? TRUE : FALSE);
return n_keycodes;
}
static void
reload_iso_next_group_combos (MetaDisplay *display)
{
const char *iso_next_group_option;
MetaKeyCombo *combos;
int *keycodes;
int n_keycodes;
int n_combos;
int i;
g_clear_pointer (&display->iso_next_group_combos, g_free);
display->n_iso_next_group_combos = 0;
iso_next_group_option = meta_prefs_get_iso_next_group_option ();
if (iso_next_group_option == NULL)
return;
n_keycodes = get_keycodes_for_keysym (display, XK_ISO_Next_Group, &keycodes);
if (g_str_equal (iso_next_group_option, "toggle") ||
g_str_equal (iso_next_group_option, "lalt_toggle") ||
g_str_equal (iso_next_group_option, "lwin_toggle") ||
g_str_equal (iso_next_group_option, "rwin_toggle") ||
g_str_equal (iso_next_group_option, "lshift_toggle") ||
g_str_equal (iso_next_group_option, "rshift_toggle") ||
g_str_equal (iso_next_group_option, "lctrl_toggle") ||
g_str_equal (iso_next_group_option, "rctrl_toggle") ||
g_str_equal (iso_next_group_option, "sclk_toggle") ||
g_str_equal (iso_next_group_option, "menu_toggle") ||
g_str_equal (iso_next_group_option, "caps_toggle"))
{
n_combos = n_keycodes;
combos = g_new (MetaKeyCombo, n_combos);
for (i = 0; i < n_keycodes; ++i)
{
combos[i].keysym = XK_ISO_Next_Group;
combos[i].keycode = keycodes[i];
combos[i].modifiers = 0;
}
}
else if (g_str_equal (iso_next_group_option, "shift_caps_toggle") ||
g_str_equal (iso_next_group_option, "shifts_toggle"))
{
n_combos = n_keycodes;
combos = g_new (MetaKeyCombo, n_combos);
for (i = 0; i < n_keycodes; ++i)
{
combos[i].keysym = XK_ISO_Next_Group;
combos[i].keycode = keycodes[i];
combos[i].modifiers = ShiftMask;
}
}
else if (g_str_equal (iso_next_group_option, "alt_caps_toggle") ||
g_str_equal (iso_next_group_option, "alt_space_toggle"))
{
n_combos = n_keycodes;
combos = g_new (MetaKeyCombo, n_combos);
for (i = 0; i < n_keycodes; ++i)
{
combos[i].keysym = XK_ISO_Next_Group;
combos[i].keycode = keycodes[i];
combos[i].modifiers = Mod1Mask;
}
}
else if (g_str_equal (iso_next_group_option, "ctrl_shift_toggle") ||
g_str_equal (iso_next_group_option, "lctrl_lshift_toggle") ||
g_str_equal (iso_next_group_option, "rctrl_rshift_toggle"))
{
n_combos = n_keycodes * 2;
combos = g_new (MetaKeyCombo, n_combos);
for (i = 0; i < n_keycodes; ++i)
{
combos[i].keysym = XK_ISO_Next_Group;
combos[i].keycode = keycodes[i];
combos[i].modifiers = ShiftMask;
combos[i + n_keycodes].keysym = XK_ISO_Next_Group;
combos[i + n_keycodes].keycode = keycodes[i];
combos[i + n_keycodes].modifiers = ControlMask;
}
}
else if (g_str_equal (iso_next_group_option, "ctrl_alt_toggle"))
{
n_combos = n_keycodes * 2;
combos = g_new (MetaKeyCombo, n_combos);
for (i = 0; i < n_keycodes; ++i)
{
combos[i].keysym = XK_ISO_Next_Group;
combos[i].keycode = keycodes[i];
combos[i].modifiers = Mod1Mask;
combos[i + n_keycodes].keysym = XK_ISO_Next_Group;
combos[i + n_keycodes].keycode = keycodes[i];
combos[i + n_keycodes].modifiers = ControlMask;
}
}
else if (g_str_equal (iso_next_group_option, "alt_shift_toggle") ||
g_str_equal (iso_next_group_option, "lalt_lshift_toggle"))
{
n_combos = n_keycodes * 2;
combos = g_new (MetaKeyCombo, n_combos);
for (i = 0; i < n_keycodes; ++i)
{
combos[i].keysym = XK_ISO_Next_Group;
combos[i].keycode = keycodes[i];
combos[i].modifiers = Mod1Mask;
combos[i + n_keycodes].keysym = XK_ISO_Next_Group;
combos[i + n_keycodes].keycode = keycodes[i];
combos[i + n_keycodes].modifiers = ShiftMask;
}
}
else
{
n_combos = 0;
combos = NULL;
}
g_free (keycodes);
display->n_iso_next_group_combos = n_combos;
display->iso_next_group_combos = combos;
}
static guint
keysym_to_keycode (MetaDisplay *display,
guint keysym)
@ -328,6 +496,8 @@ reload_keycodes (MetaDisplay *display)
display->overlay_key_combo.keycode = 0;
}
reload_iso_next_group_combos (display);
if (display->key_bindings)
{
int i;
@ -1026,6 +1196,22 @@ meta_screen_change_keygrabs (MetaScreen *screen,
display->overlay_key_combo.keycode,
display->overlay_key_combo.modifiers);
if (display->iso_next_group_combos)
{
int i = 0;
while (i < display->n_iso_next_group_combos)
{
if (display->iso_next_group_combos[i].keycode != 0)
{
meta_change_keygrab (display, screen->xroot, grab,
display->iso_next_group_combos[i].keysym,
display->iso_next_group_combos[i].keycode,
display->iso_next_group_combos[i].modifiers);
}
++i;
}
}
change_binding_keygrabs (screen->display->key_bindings,
screen->display->n_key_bindings,
screen->display, screen->xroot,
@ -1128,7 +1314,7 @@ handle_external_grab (MetaDisplay *display,
guint action = meta_display_get_keybinding_action (display,
binding->keycode,
binding->mask);
meta_display_accelerator_activate (display, action, event->deviceid);
meta_display_accelerator_activate (display, action, event->deviceid, event->time);
}
@ -1264,7 +1450,8 @@ grab_status_to_string (int status)
static gboolean
grab_keyboard (MetaDisplay *display,
Window xwindow,
guint32 timestamp)
guint32 timestamp,
int grab_mode)
{
int result;
int grab_status;
@ -1280,13 +1467,22 @@ grab_keyboard (MetaDisplay *display,
*/
meta_error_trap_push_with_return (display);
/* Strictly, we only need to set grab_mode on the keyboard device
* while the pointer should always be XIGrabModeAsync. Unfortunately
* there is a bug in the X server, only fixed (link below) in 1.15,
* which swaps these arguments for keyboard devices. As such, we set
* both the device and the paired device mode which works around
* that bug and also works on fixed X servers.
*
* http://cgit.freedesktop.org/xorg/xserver/commit/?id=9003399708936481083424b4ff8f18a16b88b7b3
*/
grab_status = XIGrabDevice (display->xdisplay,
META_VIRTUAL_CORE_KEYBOARD_ID,
xwindow,
timestamp,
None,
XIGrabModeAsync, XIGrabModeAsync,
True, /* owner_events */
grab_mode, grab_mode,
False, /* owner_events */
&mask);
if (grab_status != Success)
@ -1339,7 +1535,7 @@ meta_screen_grab_all_keys (MetaScreen *screen, guint32 timestamp)
meta_topic (META_DEBUG_KEYBINDINGS,
"Grabbing all keys on RootWindow\n");
retval = grab_keyboard (screen->display, screen->xroot, timestamp);
retval = grab_keyboard (screen->display, screen->xroot, timestamp, XIGrabModeAsync);
if (retval)
{
screen->all_keys_grabbed = TRUE;
@ -1392,7 +1588,7 @@ meta_window_grab_all_keys (MetaWindow *window,
meta_topic (META_DEBUG_KEYBINDINGS,
"Grabbing all keys on window %s\n", window->desc);
retval = grab_keyboard (window->display, grabwindow, timestamp);
retval = grab_keyboard (window->display, grabwindow, timestamp, XIGrabModeAsync);
if (retval)
{
window->keys_grabbed = FALSE;
@ -1419,6 +1615,32 @@ meta_window_ungrab_all_keys (MetaWindow *window, guint32 timestamp)
}
}
void
meta_display_freeze_keyboard (MetaDisplay *display, Window window, guint32 timestamp)
{
grab_keyboard (display, window, timestamp, XIGrabModeSync);
}
void
meta_display_ungrab_keyboard (MetaDisplay *display, guint32 timestamp)
{
ungrab_keyboard (display, timestamp);
}
void
meta_display_unfreeze_keyboard (MetaDisplay *display, guint32 timestamp)
{
meta_error_trap_push (display);
XIAllowEvents (display->xdisplay, META_VIRTUAL_CORE_KEYBOARD_ID,
XIAsyncDevice, timestamp);
/* We shouldn't need to unfreeze the pointer device here, however we
* have to, due to the workaround we do in grab_keyboard().
*/
XIAllowEvents (display->xdisplay, META_VIRTUAL_CORE_POINTER_ID,
XIAsyncDevice, timestamp);
meta_error_trap_pop (display);
}
static gboolean
is_modifier (MetaDisplay *display,
unsigned int keycode)
@ -1765,6 +1987,41 @@ process_overlay_key (MetaDisplay *display,
return FALSE;
}
static gboolean
process_iso_next_group (MetaDisplay *display,
MetaScreen *screen,
XIDeviceEvent *event,
KeySym keysym)
{
gboolean activate;
unsigned int mods;
int i;
if (event->evtype != XI_KeyPress)
return FALSE;
activate = FALSE;
mods = (event->mods.effective & 0xff & ~(display->ignored_modifier_mask));
for (i = 0; i < display->n_iso_next_group_combos; ++i)
{
if (event->detail == (int)display->iso_next_group_combos[i].keycode &&
mods == display->iso_next_group_combos[i].modifiers)
{
/* If the signal handler returns TRUE the keyboard will
remain frozen. It's the signal handler's responsibility
to unfreeze it. */
if (!meta_display_modifiers_accelerator_activate (display))
XIAllowEvents (display->xdisplay, event->deviceid,
XIAsyncDevice, event->time);
activate = TRUE;
break;
}
}
return activate;
}
/* Handle a key event. May be called recursively: some key events cause
* grabs to be ended and then need to be processed again in their own
* right. This cannot cause infinite recursion because we never call
@ -1791,21 +2048,6 @@ meta_display_process_key_event (MetaDisplay *display,
const char *str;
MetaScreen *screen;
if (all_bindings_disabled)
{
/* In this mode, we try to pretend we don't have grabs, so we
* immediately replay events and drop the grab. (This still
* messes up global passive grabs from other clients.) The
* FALSE return here is a little suspect, but we don't really
* know if we'll see the event again or not, and it's pretty
* poorly defined how this mode is supposed to interact with
* plugins.
*/
XIAllowEvents (display->xdisplay, event->deviceid,
XIReplayDevice, event->time);
return FALSE;
}
/* if key event was on root window, we have a shortcut */
screen = meta_display_screen_for_root (display, event->event);
@ -1839,6 +2081,10 @@ meta_display_process_key_event (MetaDisplay *display,
handled = process_overlay_key (display, screen, event, keysym);
if (handled)
return TRUE;
handled = process_iso_next_group (display, screen, event, keysym);
if (handled)
return TRUE;
}
XIAllowEvents (display->xdisplay, event->deviceid,
@ -3839,13 +4085,39 @@ handle_set_spew_mark (MetaDisplay *display,
meta_verbose ("-- MARK MARK MARK MARK --\n");
}
void
meta_set_keybindings_disabled (gboolean setting)
#ifdef HAVE_WAYLAND
static void
handle_switch_vt (MetaDisplay *display,
MetaScreen *screen,
MetaWindow *window,
XIDeviceEvent *event,
MetaKeyBinding *binding,
gpointer dummy)
{
all_bindings_disabled = setting;
meta_topic (META_DEBUG_KEYBINDINGS,
"Keybindings %s\n", all_bindings_disabled ? "disabled" : "enabled");
gint vt = binding->handler->data;
MetaWaylandCompositor *compositor;
MetaTTY *tty;
compositor = meta_wayland_compositor_get_default ();
tty = meta_wayland_compositor_get_tty (compositor);
if (tty)
{
GError *error;
error = NULL;
if (!meta_tty_activate_vt (tty, vt, &error))
{
g_warning ("Failed to switch VT: %s", error->message);
g_error_free (error);
}
}
else
{
g_debug ("Ignoring VT switch keybinding, not running as VT manager");
}
}
#endif
/**
* meta_keybindings_set_custom_handler:
@ -4171,6 +4443,60 @@ init_builtin_key_bindings (MetaDisplay *display)
META_KEYBINDING_ACTION_SET_SPEW_MARK,
handle_set_spew_mark, 0);
#ifdef HAVE_WAYLAND
if (meta_is_wayland_compositor ())
{
add_builtin_keybinding (display,
"switch-to-session-1",
mutter_keybindings,
META_KEY_BINDING_NONE,
META_KEYBINDING_ACTION_NONE,
handle_switch_vt, 1);
add_builtin_keybinding (display,
"switch-to-session-2",
mutter_keybindings,
META_KEY_BINDING_NONE,
META_KEYBINDING_ACTION_NONE,
handle_switch_vt, 2);
add_builtin_keybinding (display,
"switch-to-session-3",
mutter_keybindings,
META_KEY_BINDING_NONE,
META_KEYBINDING_ACTION_NONE,
handle_switch_vt, 3);
add_builtin_keybinding (display,
"switch-to-session-4",
mutter_keybindings,
META_KEY_BINDING_NONE,
META_KEYBINDING_ACTION_NONE,
handle_switch_vt, 4);
add_builtin_keybinding (display,
"switch-to-session-5",
mutter_keybindings,
META_KEY_BINDING_NONE,
META_KEYBINDING_ACTION_NONE,
handle_switch_vt, 5);
add_builtin_keybinding (display,
"switch-to-session-6",
mutter_keybindings,
META_KEY_BINDING_NONE,
META_KEYBINDING_ACTION_NONE,
handle_switch_vt, 6);
add_builtin_keybinding (display,
"switch-to-session-7",
mutter_keybindings,
META_KEY_BINDING_NONE,
META_KEYBINDING_ACTION_NONE,
handle_switch_vt, 7);
}
#endif
#undef REVERSES_AND_REVERSED
/************************ PER WINDOW BINDINGS ************************/
@ -4532,6 +4858,12 @@ meta_display_init_keys (MetaDisplay *display)
g_hash_table_insert (key_handlers, g_strdup ("overlay-key"), handler);
handler = g_new0 (MetaKeyHandler, 1);
handler->name = g_strdup ("iso-next-group");
handler->flags = META_KEY_BINDING_BUILTIN;
g_hash_table_insert (key_handlers, g_strdup ("iso-next-group"), handler);
handler = g_new0 (MetaKeyHandler, 1);
handler->name = g_strdup ("external-grab");
handler->func = handle_external_grab;

View File

@ -55,8 +55,12 @@
#include "session.h"
#include <meta/prefs.h>
#include <meta/compositor.h>
#ifdef HAVE_WAYLAND
#include "meta-wayland-private.h"
#endif
#include <glib-object.h>
#include <glib-unix.h>
#include <gdk/gdkx.h>
#include <stdlib.h>
@ -346,28 +350,41 @@ meta_finalize (void)
if (display)
meta_display_close (display,
CurrentTime); /* I doubt correct timestamps matter here */
}
static int sigterm_pipe_fds[2] = { -1, -1 };
static void
sigterm_handler (int signum)
{
if (sigterm_pipe_fds[1] >= 0)
{
int G_GNUC_UNUSED dummy;
dummy = write (sigterm_pipe_fds[1], "", 1);
close (sigterm_pipe_fds[1]);
sigterm_pipe_fds[1] = -1;
}
#ifdef HAVE_WAYLAND
if (meta_is_wayland_compositor ())
meta_wayland_finalize ();
#endif
}
static gboolean
on_sigterm (void)
on_sigterm (gpointer user_data)
{
meta_quit (META_EXIT_SUCCESS);
return FALSE;
meta_quit (EXIT_SUCCESS);
return G_SOURCE_REMOVE;
}
static void
crash_handler (int signum)
{
char buffer[256];
MetaWaylandCompositor *compositor;
MetaTTY *tty;
snprintf (buffer, 256, "Fatal server error: %d\n", signum);
write (STDERR_FILENO, buffer, strlen (buffer));
compositor = meta_wayland_compositor_get_default ();
tty = meta_wayland_compositor_get_tty (compositor);
/* Passing FALSE ensures that we only do ioctls, which is
safe from a signal handler */
if (tty)
meta_tty_reset (tty, FALSE);
/* We can't continue with the default handling, so just exit here */
_exit(1);
}
/**
@ -381,7 +398,6 @@ meta_init (void)
{
struct sigaction act;
sigset_t empty_mask;
GIOChannel *channel;
sigemptyset (&empty_mask);
act.sa_handler = SIG_IGN;
@ -396,20 +412,20 @@ meta_init (void)
g_strerror (errno));
#endif
if (pipe (sigterm_pipe_fds) != 0)
g_printerr ("Failed to create SIGTERM pipe: %s\n",
g_strerror (errno));
if (meta_is_wayland_compositor ())
{
act.sa_handler = crash_handler;
channel = g_io_channel_unix_new (sigterm_pipe_fds[0]);
g_io_channel_set_flags (channel, G_IO_FLAG_NONBLOCK, NULL);
g_io_add_watch (channel, G_IO_IN, (GIOFunc) on_sigterm, NULL);
g_io_channel_set_close_on_unref (channel, TRUE);
g_io_channel_unref (channel);
/* Ignore if we can't register signal handlers, worse
that can happen one needs the sysrq to get out of the VT */
sigaction (SIGABRT, &act, NULL);
sigaction (SIGSEGV, &act, NULL);
sigaction (SIGBUS, &act, NULL);
sigaction (SIGFPE, &act, NULL);
sigaction (SIGTRAP, &act, NULL);
}
act.sa_handler = &sigterm_handler;
if (sigaction (SIGTERM, &act, NULL) < 0)
g_printerr ("Failed to register SIGTERM handler: %s\n",
g_strerror (errno));
g_unix_signal_add (SIGTERM, on_sigterm, NULL);
if (g_getenv ("MUTTER_VERBOSE"))
meta_set_verbose (TRUE);
@ -427,9 +443,18 @@ meta_init (void)
g_irepository_prepend_search_path (MUTTER_PKGLIBDIR);
#endif
meta_set_syncing (opt_sync || (g_getenv ("MUTTER_SYNC") != NULL));
#ifdef HAVE_WAYLAND
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
#endif
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 +466,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 ();
}
}
/**
@ -514,14 +546,14 @@ meta_run (void)
if (g_getenv ("MUTTER_G_FATAL_WARNINGS") != NULL)
g_log_set_always_fatal (G_LOG_LEVEL_MASK);
meta_ui_set_current_theme (meta_prefs_get_theme (), FALSE);
meta_ui_set_current_theme (meta_prefs_get_theme ());
/* Try to find some theme that'll work if the theme preference
* doesn't exist. First try Simple (the default theme) then just
* try anything in the themes directory.
*/
if (!meta_ui_have_a_theme ())
meta_ui_set_current_theme ("Simple", FALSE);
meta_ui_set_current_theme ("Simple");
if (!meta_ui_have_a_theme ())
{
@ -539,7 +571,7 @@ meta_run (void)
while (((dir_entry = g_dir_read_name (themes_dir)) != NULL) &&
(!meta_ui_have_a_theme ()))
{
meta_ui_set_current_theme (dir_entry, FALSE);
meta_ui_set_current_theme (dir_entry);
}
g_dir_close (themes_dir);
@ -598,7 +630,7 @@ prefs_changed_callback (MetaPreference pref,
{
case META_PREF_THEME:
case META_PREF_DRAGGABLE_BORDER_WIDTH:
meta_ui_set_current_theme (meta_prefs_get_theme (), FALSE);
meta_ui_set_current_theme (meta_prefs_get_theme ());
meta_display_retheme_all ();
break;

View File

@ -0,0 +1,46 @@
/* -*- 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>
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,451 @@
/* -*- 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"
#include "meta-wayland-private.h"
#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);
}
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;
}
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;
if (meta_is_wayland_compositor ())
self = make_wayland_cursor_tracker (screen);
else
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_DATADIR,
"mutter/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,932 @@
/* -*- 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 GINT64_TO_XSYNCVALUE(value, ret) XSyncIntsToValue (ret, value, ((guint64)value) >> 32)
static void
fire_watch (MetaIdleMonitorWatch *watch)
{
MetaIdleMonitor *monitor;
monitor = watch->monitor;
g_object_ref (monitor);
if (watch->callback)
{
watch->callback (watch->monitor,
watch->id,
watch->user_data);
}
if (watch->timeout_msec == 0)
meta_idle_monitor_remove_watch (watch->monitor, watch->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;
GINT64_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, monitor);
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)
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
*/
guint64
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
on_device_added (ClutterDeviceManager *device_manager,
ClutterInputDevice *device,
GDBusObjectManagerServer *manager)
{
MetaDBusIdleMonitor *skeleton;
MetaIdleMonitor *monitor;
MetaDBusObjectSkeleton *object;
int device_id;
gboolean is_core;
char *path;
is_core = clutter_input_device_get_device_mode (device) == CLUTTER_INPUT_MODE_MASTER;
if (is_core)
{
monitor = meta_idle_monitor_get_core ();
path = g_strdup ("/org/gnome/Mutter/IdleMonitor/Core");
}
else
{
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);
}
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_bus_acquired (GDBusConnection *connection,
const char *name,
gpointer user_data)
{
GDBusObjectManagerServer *manager;
ClutterDeviceManager *device_manager;
GSList *devices, *iter;
manager = g_dbus_object_manager_server_new ("/org/gnome/Mutter/IdleMonitor");
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_dbus_object_manager_server_set_connection (manager, g_bus_get_sync (G_BUS_TYPE_SESSION, NULL, NULL));
}
static void
on_name_acquired (GDBusConnection *connection,
const char *name,
gpointer user_data)
{
meta_topic (META_DEBUG_DBUS, "Acquired name %s\n", name);
}
static void
on_name_lost (GDBusConnection *connection,
const char *name,
gpointer user_data)
{
meta_topic (META_DEBUG_DBUS, "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

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

File diff suppressed because it is too large Load Diff

767
src/core/monitor-kms.c Normal file
View File

@ -0,0 +1,767 @@
/* -*- 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>
*/
#include "config.h"
#include <string.h>
#include <stdlib.h>
#include <clutter/clutter.h>
#include <errno.h>
#include <sys/ioctl.h>
#include <sys/mman.h>
#include <unistd.h>
#include <xf86drm.h>
#include <xf86drmMode.h>
#include <meta/main.h>
#include <meta/errors.h>
#include "monitor-private.h"
#define ALL_WL_TRANSFORMS ((1 << (WL_OUTPUT_TRANSFORM_FLIPPED_270 + 1)) - 1)
typedef struct {
drmModeConnector *connector;
unsigned n_encoders;
drmModeEncoderPtr *encoders;
drmModeEncoderPtr current_encoder;
/* bitmasks of encoder position in the resources array */
uint32_t encoder_mask;
uint32_t enc_clone_mask;
uint32_t dpms_prop_id;
} MetaOutputKms;
struct _MetaMonitorManagerKms
{
MetaMonitorManager parent_instance;
int fd;
drmModeConnector **connectors;
unsigned int n_connectors;
drmModeEncoder **encoders;
unsigned int n_encoders;
drmModeEncoder *current_encoder;
};
struct _MetaMonitorManagerKmsClass
{
MetaMonitorManagerClass parent_class;
};
G_DEFINE_TYPE (MetaMonitorManagerKms, meta_monitor_manager_kms, META_TYPE_MONITOR_MANAGER);
static int
compare_outputs (const void *one,
const void *two)
{
const MetaOutput *o_one = one, *o_two = two;
return strcmp (o_one->name, o_two->name);
}
static char *
make_output_name (drmModeConnector *connector)
{
static const char * const connector_type_names[] = {
"unknown", "VGA", "DVII", "DVID", "DVID", "Composite",
"SVIDEO", "LVDS", "Component", "9PinDIN", "DisplayPort",
"HDMIA", "HDMIB", "TV", "eDP"
};
const char *connector_type_name;
if (connector->connector_type >= 0 &&
connector->connector_type < G_N_ELEMENTS (connector_type_names))
connector_type_name = connector_type_names[connector->connector_type];
else
connector_type_name = "unknown";
return g_strdup_printf ("%s%d", connector_type_name, connector->connector_id);
}
static void
meta_output_destroy_notify (MetaOutput *output)
{
MetaOutputKms *output_kms;
unsigned i;
output_kms = output->driver_private;
for (i = 0; i < output_kms->n_encoders; i++)
drmModeFreeEncoder (output_kms->encoders[i]);
g_slice_free (MetaOutputKms, output_kms);
}
static void
meta_monitor_mode_destroy_notify (MetaMonitorMode *output)
{
g_slice_free (drmModeModeInfo, output->driver_private);
}
static gboolean
drm_mode_equal (gconstpointer one,
gconstpointer two)
{
return memcmp (one, two, sizeof (drmModeModeInfo)) == 0;
}
static guint
drm_mode_hash (gconstpointer ptr)
{
const drmModeModeInfo *mode = ptr;
guint hash = 0;
hash ^= mode->clock;
hash ^= mode->hdisplay ^ mode->hsync_start ^ mode->hsync_end;
hash ^= mode->vdisplay ^ mode->vsync_start ^ mode->vsync_end;
hash ^= mode->vrefresh;
hash ^= mode->flags ^ mode->type;
return hash;
}
static void
meta_monitor_manager_kms_read_current (MetaMonitorManager *manager)
{
MetaMonitorManagerKms *manager_kms = META_MONITOR_MANAGER_KMS (manager);
drmModeRes *resources;
GHashTable *modes;
GHashTableIter iter;
drmModeModeInfo *mode;
unsigned int i, j, k;
unsigned int n_actual_outputs;
int width, height;
resources = drmModeGetResources(manager_kms->fd);
modes = g_hash_table_new (drm_mode_hash, drm_mode_equal);
manager->max_screen_width = resources->max_width;
manager->max_screen_height = resources->max_height;
manager->power_save_mode = META_POWER_SAVE_ON;
manager_kms->n_connectors = resources->count_connectors;
manager_kms->connectors = g_new (drmModeConnector *, manager_kms->n_connectors);
for (i = 0; i < manager_kms->n_connectors; i++)
{
drmModeConnector *connector;
connector = drmModeGetConnector (manager_kms->fd, resources->connectors[i]);
manager_kms->connectors[i] = connector;
if (connector->connection == DRM_MODE_CONNECTED)
{
/* Collect all modes for this connector */
for (j = 0; j < (unsigned)connector->count_modes; j++)
g_hash_table_add (modes, &connector->modes[j]);
}
}
manager_kms->n_encoders = resources->count_encoders;
manager_kms->encoders = g_new (drmModeEncoder *, manager_kms->n_encoders);
for (i = 0; i < manager_kms->n_encoders; i++)
{
manager_kms->encoders[i] = drmModeGetEncoder (manager_kms->fd,
resources->encoders[i]);
}
manager->n_modes = g_hash_table_size (modes);
manager->modes = g_new0 (MetaMonitorMode, manager->n_modes);
g_hash_table_iter_init (&iter, modes);
i = 0;
while (g_hash_table_iter_next (&iter, NULL, (gpointer)&mode))
{
MetaMonitorMode *meta_mode;
meta_mode = &manager->modes[i];
meta_mode->mode_id = i;
meta_mode->name = g_strndup (mode->name, DRM_DISPLAY_MODE_LEN);
meta_mode->width = mode->hdisplay;
meta_mode->height = mode->vdisplay;
meta_mode->refresh_rate = (1000 * mode->clock /
((float)mode->htotal * mode->vtotal));
meta_mode->driver_private = g_slice_dup (drmModeModeInfo, mode);
meta_mode->driver_notify = (GDestroyNotify)meta_monitor_mode_destroy_notify;
i++;
}
g_hash_table_destroy (modes);
manager->n_crtcs = resources->count_crtcs;
manager->crtcs = g_new0 (MetaCRTC, manager->n_crtcs);
width = 0; height = 0;
for (i = 0; i < (unsigned)resources->count_crtcs; i++)
{
drmModeCrtc *crtc;
MetaCRTC *meta_crtc;
crtc = drmModeGetCrtc (manager_kms->fd, resources->crtcs[i]);
meta_crtc = &manager->crtcs[i];
meta_crtc->crtc_id = crtc->crtc_id;
meta_crtc->rect.x = crtc->x;
meta_crtc->rect.y = crtc->y;
meta_crtc->rect.width = crtc->width;
meta_crtc->rect.height = crtc->height;
meta_crtc->dirty = FALSE;
/* FIXME: we can handle some transforms, with a combination of
scaling and fitting, but it is very driver dependent */
meta_crtc->transform = WL_OUTPUT_TRANSFORM_NORMAL;
meta_crtc->all_transforms = 1 << WL_OUTPUT_TRANSFORM_NORMAL;
if (crtc->mode_valid)
{
for (j = 0; j < manager->n_modes; j++)
{
if (drm_mode_equal (&crtc->mode, manager->modes[j].driver_private))
{
meta_crtc->current_mode = &manager->modes[j];
break;
}
}
width = MAX (width, meta_crtc->rect.x + meta_crtc->rect.width);
height = MAX (height, meta_crtc->rect.y + meta_crtc->rect.height);
}
drmModeFreeCrtc (crtc);
}
manager->screen_width = width;
manager->screen_height = height;
manager->outputs = g_new0 (MetaOutput, manager_kms->n_connectors);
n_actual_outputs = 0;
for (i = 0; i < manager_kms->n_connectors; i++)
{
MetaOutput *meta_output;
MetaOutputKms *output_kms;
drmModeConnector *connector;
GArray *crtcs;
unsigned int crtc_mask;
connector = manager_kms->connectors[i];
meta_output = &manager->outputs[n_actual_outputs];
if (connector->connection == DRM_MODE_CONNECTED)
{
meta_output->output_id = connector->connector_id;
meta_output->name = make_output_name (connector);
meta_output->vendor = g_strdup ("unknown");
meta_output->product = g_strdup ("unknown");
meta_output->serial = g_strdup ("");
meta_output->width_mm = connector->mmWidth;
meta_output->height_mm = connector->mmHeight;
if (connector->subpixel == DRM_MODE_SUBPIXEL_UNKNOWN)
meta_output->subpixel_order = COGL_SUBPIXEL_ORDER_UNKNOWN;
else if (connector->subpixel == DRM_MODE_SUBPIXEL_NONE)
meta_output->subpixel_order = COGL_SUBPIXEL_ORDER_NONE;
else
meta_output->subpixel_order = connector->subpixel;
meta_output->n_modes = connector->count_modes;
meta_output->modes = g_new0 (MetaMonitorMode *, meta_output->n_modes);
for (j = 0; j < meta_output->n_modes; j++)
{
for (k = 0; k < manager->n_modes; k++)
{
if (drm_mode_equal (&connector->modes[j], manager->modes[k].driver_private))
{
meta_output->modes[j] = &manager->modes[k];
break;
}
}
}
meta_output->preferred_mode = meta_output->modes[0];
meta_output->driver_private = output_kms = g_slice_new0 (MetaOutputKms);
meta_output->driver_notify = (GDestroyNotify)meta_output_destroy_notify;
output_kms->connector = connector;
output_kms->n_encoders = connector->count_encoders;
output_kms->encoders = g_new0 (drmModeEncoderPtr, output_kms->n_encoders);
crtc_mask = 0x7F;
for (j = 0; j < output_kms->n_encoders; j++)
{
output_kms->encoders[j] = drmModeGetEncoder (manager_kms->fd, connector->encoders[j]);
crtc_mask &= output_kms->encoders[j]->possible_crtcs;
if (output_kms->encoders[j]->encoder_id == connector->encoder_id)
output_kms->current_encoder = output_kms->encoders[j];
}
crtcs = g_array_new (FALSE, FALSE, sizeof (MetaCRTC*));
for (j = 0; j < manager->n_crtcs; j++)
{
if (crtc_mask & (1 << j))
{
MetaCRTC *crtc = &manager->crtcs[j];
g_array_append_val (crtcs, crtc);
}
}
meta_output->n_possible_crtcs = crtcs->len;
meta_output->possible_crtcs = (void*)g_array_free (crtcs, FALSE);
if (output_kms->current_encoder && output_kms->current_encoder->crtc_id != 0)
{
for (j = 0; j < manager->n_crtcs; j++)
{
if (manager->crtcs[j].crtc_id == output_kms->current_encoder->crtc_id)
{
meta_output->crtc = &manager->crtcs[j];
break;
}
}
}
else
meta_output->crtc = NULL;
meta_output->is_primary = FALSE;
meta_output->is_presentation = FALSE;
for (j = 0; j < (unsigned)connector->count_props; j++)
{
drmModePropertyPtr prop;
prop = drmModeGetProperty(manager_kms->fd, connector->props[j]);
if (prop)
{
if ((prop->flags & DRM_MODE_PROP_ENUM) &&
strcmp(prop->name, "DPMS") == 0)
{
output_kms->dpms_prop_id = prop->prop_id;
drmModeFreeProperty(prop);
break;
}
drmModeFreeProperty(prop);
}
}
/* FIXME: backlight is a very driver specific thing unfortunately,
every DDX does its own thing, and the dumb KMS API does not include it.
For example, xf86-video-intel has a list of paths to probe in /sys/class/backlight
(one for each major HW maker, and then some).
We can't do the same because we're not root.
It might be best to leave backlight out of the story and rely on the setuid
helper in gnome-settings-daemon.
*/
meta_output->backlight_min = 0;
meta_output->backlight_max = 0;
meta_output->backlight = -1;
n_actual_outputs++;
}
}
manager->n_outputs = n_actual_outputs;
manager->outputs = g_renew (MetaOutput, manager->outputs, manager->n_outputs);
/* Sort the outputs for easier handling in MetaMonitorConfig */
qsort (manager->outputs, manager->n_outputs, sizeof (MetaOutput), compare_outputs);
/* Now fix the clones.
Code mostly inspired by xf86-video-modesetting. */
/* XXX: intel hardware doesn't usually have clones, but we only have intel
cards, so this code was never tested! */
for (i = 0; i < manager->n_outputs; i++)
{
MetaOutput *meta_output;
MetaOutputKms *output_kms;
meta_output = &manager->outputs[i];
output_kms = meta_output->driver_private;
output_kms->enc_clone_mask = 0xff;
output_kms->encoder_mask = 0;
for (j = 0; j < output_kms->n_encoders; j++)
{
for (k = 0; k < manager_kms->n_encoders; k++)
{
if (output_kms->encoders[j]->encoder_id == manager_kms->encoders[k]->encoder_id)
{
output_kms->encoder_mask |= (1 << k);
break;
}
}
output_kms->enc_clone_mask &= output_kms->encoders[j]->possible_clones;
}
}
for (i = 0; i < manager->n_outputs; i++)
{
MetaOutput *meta_output;
MetaOutputKms *output_kms;
meta_output = &manager->outputs[i];
output_kms = meta_output->driver_private;
if (output_kms->enc_clone_mask == 0)
continue;
for (j = 0; j < manager->n_outputs; j++)
{
MetaOutput *meta_clone;
MetaOutputKms *clone_kms;
meta_clone = &manager->outputs[i];
clone_kms = meta_clone->driver_private;
if (meta_clone == meta_output)
continue;
if (clone_kms->encoder_mask == 0)
continue;
if (clone_kms->encoder_mask == output_kms->enc_clone_mask)
{
meta_output->n_possible_clones++;
meta_output->possible_clones = g_renew (MetaOutput *,
meta_output->possible_clones,
meta_output->n_possible_clones);
meta_output->possible_clones[meta_output->n_possible_clones - 1] = meta_clone;
}
}
}
drmModeFreeResources (resources);
}
static void
meta_monitor_manager_kms_set_power_save_mode (MetaMonitorManager *manager,
MetaPowerSave mode)
{
MetaMonitorManagerKms *manager_kms = META_MONITOR_MANAGER_KMS (manager);
uint64_t state;
unsigned i;
switch (mode) {
case META_POWER_SAVE_ON:
state = DRM_MODE_DPMS_ON;
break;
case META_POWER_SAVE_STANDBY:
state = DRM_MODE_DPMS_STANDBY;
break;
case META_POWER_SAVE_SUSPEND:
state = DRM_MODE_DPMS_SUSPEND;
break;
case META_POWER_SAVE_OFF:
state = DRM_MODE_DPMS_SUSPEND;
break;
default:
return;
}
for (i = 0; i < manager->n_outputs; i++)
{
MetaOutput *meta_output;
MetaOutputKms *output_kms;
meta_output = &manager->outputs[i];
output_kms = meta_output->driver_private;
if (output_kms->dpms_prop_id)
{
int ok = drmModeConnectorSetProperty(manager_kms->fd, meta_output->output_id,
output_kms->dpms_prop_id, state);
if (ok < 0)
meta_warning ("Failed to set power save mode for output %s: %s\n",
meta_output->name, strerror (errno));
}
}
}
static void
crtc_free (CoglKmsCrtc *crtc)
{
g_free (crtc->connectors);
g_slice_free (CoglKmsCrtc, crtc);
}
static void
meta_monitor_manager_kms_apply_configuration (MetaMonitorManager *manager,
MetaCRTCInfo **crtcs,
unsigned int n_crtcs,
MetaOutputInfo **outputs,
unsigned int n_outputs)
{
ClutterBackend *backend;
CoglContext *cogl_context;
CoglDisplay *cogl_display;
unsigned i;
GList *cogl_crtcs;
int width, height;
gboolean ok;
GError *error;
cogl_crtcs = NULL;
width = 0; height = 0;
for (i = 0; i < n_crtcs; i++)
{
MetaCRTCInfo *crtc_info = crtcs[i];
MetaCRTC *crtc = crtc_info->crtc;
CoglKmsCrtc *cogl_crtc;
crtc->dirty = TRUE;
cogl_crtc = g_slice_new0 (CoglKmsCrtc);
cogl_crtcs = g_list_prepend (cogl_crtcs, cogl_crtc);
if (crtc_info->mode == NULL)
{
cogl_crtc->id = crtc->crtc_id;
cogl_crtc->x = 0;
cogl_crtc->y = 0;
cogl_crtc->count = 0;
memset (&cogl_crtc->mode, 0, sizeof (drmModeModeInfo));
cogl_crtc->connectors = NULL;
cogl_crtc->count = 0;
crtc->rect.x = 0;
crtc->rect.y = 0;
crtc->rect.width = 0;
crtc->rect.height = 0;
crtc->current_mode = NULL;
}
else
{
MetaMonitorMode *mode;
uint32_t *outputs;
unsigned int j, n_outputs;
mode = crtc_info->mode;
cogl_crtc->id = crtc->crtc_id;
cogl_crtc->x = crtc_info->x;
cogl_crtc->y = crtc_info->y;
cogl_crtc->count = n_outputs = crtc_info->outputs->len;
cogl_crtc->connectors = outputs = g_new (uint32_t, n_outputs);
for (j = 0; j < n_outputs; j++)
{
MetaOutput *output = ((MetaOutput**)crtc_info->outputs->pdata)[j];
outputs[j] = output->output_id;
output->dirty = TRUE;
output->crtc = crtc;
}
memcpy (&cogl_crtc->mode, crtc_info->mode->driver_private,
sizeof (drmModeModeInfo));
width = MAX (width, crtc_info->x + crtc_info->mode->width);
height = MAX (height, crtc_info->y + crtc_info->mode->height);
crtc->rect.x = crtc_info->x;
crtc->rect.y = crtc_info->y;
crtc->rect.width = mode->width;
crtc->rect.height = mode->height;
crtc->current_mode = mode;
crtc->transform = crtc_info->transform;
}
}
/* Disable CRTCs not mentioned in the list */
for (i = 0; i < manager->n_crtcs; i++)
{
MetaCRTC *crtc = &manager->crtcs[i];
CoglKmsCrtc *cogl_crtc;
crtc->logical_monitor = NULL;
if (crtc->dirty)
{
crtc->dirty = FALSE;
continue;
}
cogl_crtc = g_slice_new0 (CoglKmsCrtc);
cogl_crtcs = g_list_prepend (cogl_crtcs, cogl_crtc);
cogl_crtc->id = crtc->crtc_id;
cogl_crtc->x = 0;
cogl_crtc->y = 0;
cogl_crtc->count = 0;
memset (&cogl_crtc->mode, 0, sizeof (drmModeModeInfo));
cogl_crtc->connectors = NULL;
cogl_crtc->count = 0;
crtc->rect.x = 0;
crtc->rect.y = 0;
crtc->rect.width = 0;
crtc->rect.height = 0;
crtc->current_mode = NULL;
}
backend = clutter_get_default_backend ();
cogl_context = clutter_backend_get_cogl_context (backend);
cogl_display = cogl_context_get_display (cogl_context);
error = NULL;
ok = cogl_kms_display_set_layout (cogl_display, width, height, cogl_crtcs, &error);
g_list_free_full (cogl_crtcs, (GDestroyNotify) crtc_free);
if (!ok)
{
meta_warning ("Applying display configuration failed: %s\n", error->message);
g_error_free (error);
return;
}
for (i = 0; i < n_outputs; i++)
{
MetaOutputInfo *output_info = outputs[i];
MetaOutput *output = output_info->output;
output->is_primary = output_info->is_primary;
output->is_presentation = output_info->is_presentation;
}
/* Disable outputs not mentioned in the list */
for (i = 0; i < manager->n_outputs; i++)
{
MetaOutput *output = &manager->outputs[i];
if (output->dirty)
{
output->dirty = FALSE;
continue;
}
output->crtc = NULL;
output->is_primary = FALSE;
}
manager->screen_width = width;
manager->screen_height = height;
meta_monitor_manager_rebuild_derived (manager);
}
static void
meta_monitor_manager_kms_get_crtc_gamma (MetaMonitorManager *manager,
MetaCRTC *crtc,
gsize *size,
unsigned short **red,
unsigned short **green,
unsigned short **blue)
{
MetaMonitorManagerKms *manager_kms = META_MONITOR_MANAGER_KMS (manager);
drmModeCrtc *kms_crtc;
kms_crtc = drmModeGetCrtc (manager_kms->fd, crtc->crtc_id);
*size = kms_crtc->gamma_size;
*red = g_new (unsigned short, *size);
*green = g_new (unsigned short, *size);
*blue = g_new (unsigned short, *size);
drmModeCrtcGetGamma (manager_kms->fd, crtc->crtc_id, *size, *red, *green, *blue);
drmModeFreeCrtc (kms_crtc);
}
static void
meta_monitor_manager_kms_set_crtc_gamma (MetaMonitorManager *manager,
MetaCRTC *crtc,
gsize size,
unsigned short *red,
unsigned short *green,
unsigned short *blue)
{
MetaMonitorManagerKms *manager_kms = META_MONITOR_MANAGER_KMS (manager);
drmModeCrtcSetGamma (manager_kms->fd, crtc->crtc_id, size, red, green, blue);
}
static void
meta_monitor_manager_kms_init (MetaMonitorManagerKms *manager_kms)
{
ClutterBackend *backend;
CoglContext *cogl_context;
CoglDisplay *cogl_display;
CoglRenderer *cogl_renderer;
backend = clutter_get_default_backend ();
cogl_context = clutter_backend_get_cogl_context (backend);
cogl_display = cogl_context_get_display (cogl_context);
cogl_renderer = cogl_display_get_renderer (cogl_display);
manager_kms->fd = cogl_kms_renderer_get_kms_fd (cogl_renderer);
}
static void
meta_monitor_manager_kms_finalize (GObject *object)
{
MetaMonitorManagerKms *manager_kms = META_MONITOR_MANAGER_KMS (object);
unsigned i;
for (i = 0; i < manager_kms->n_encoders; i++)
drmModeFreeEncoder (manager_kms->encoders[i]);
for (i = 0; i < manager_kms->n_connectors; i++)
drmModeFreeConnector (manager_kms->connectors[i]);
g_free (manager_kms->encoders);
g_free (manager_kms->connectors);
G_OBJECT_CLASS (meta_monitor_manager_kms_parent_class)->finalize (object);
}
static void
meta_monitor_manager_kms_class_init (MetaMonitorManagerKmsClass *klass)
{
MetaMonitorManagerClass *manager_class = META_MONITOR_MANAGER_CLASS (klass);
GObjectClass *object_class = G_OBJECT_CLASS (klass);
object_class->finalize = meta_monitor_manager_kms_finalize;
manager_class->read_current = meta_monitor_manager_kms_read_current;
manager_class->apply_configuration = meta_monitor_manager_kms_apply_configuration;
manager_class->set_power_save_mode = meta_monitor_manager_kms_set_power_save_mode;
manager_class->get_crtc_gamma = meta_monitor_manager_kms_get_crtc_gamma;
manager_class->set_crtc_gamma = meta_monitor_manager_kms_set_crtc_gamma;
}

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

@ -0,0 +1,408 @@
/* -*- 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 "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 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;
gpointer driver_private;
GDestroyNotify driver_notify;
};
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 dirty;
};
struct _MetaMonitorMode
{
/* The low-level ID of this mode, used to apply back configuration */
glong mode_id;
char *name;
int width;
int height;
float refresh_rate;
gpointer driver_private;
GDestroyNotify driver_notify;
};
/**
* 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.
This is a XID when using XRandR, otherwise a KMS id (not implemented).
In any case, 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;
};
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);
void meta_monitor_manager_init_dbus (MetaMonitorManager *manager,
GAsyncReadyCallback callback,
gpointer user_data);
gboolean meta_monitor_manager_init_dbus_finish (MetaMonitorManager *manager,
GAsyncResult *result,
GError **error);
void meta_monitor_manager_rebuild_derived (MetaMonitorManager *manager);
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_MANAGER_KMS (meta_monitor_manager_kms_get_type ())
#define META_MONITOR_MANAGER_KMS(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), META_TYPE_MONITOR_MANAGER_KMS, MetaMonitorManagerKms))
#define META_MONITOR_MANAGER_KMS_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), META_TYPE_MONITOR_MANAGER_KMS, MetaMonitorManagerKmsClass))
#define META_IS_MONITOR_MANAGER_KMS(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), META_TYPE_MONITOR_MANAGER_KMS))
#define META_IS_MONITOR_MANAGER_KMS_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), META_TYPE_MONITOR_MANAGER_KMS))
#define META_MONITOR_MANAGER_KMS_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), META_TYPE_MONITOR_MANAGER_KMS, MetaMonitorManagerKmsClass))
typedef struct _MetaMonitorManagerKmsClass MetaMonitorManagerKmsClass;
typedef struct _MetaMonitorManagerKms MetaMonitorManagerKms;
GType meta_monitor_manager_kms_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);
#endif

846
src/core/monitor-xrandr.c Normal file
View File

@ -0,0 +1,846 @@
/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
/*
* Copyright (C) 2001, 2002 Havoc Pennington
* Copyright (C) 2002, 2003 Red Hat Inc.
* Some ICCCM manager selection code derived from fvwm2,
* Copyright (C) 2001 Dominik Vogt, Matthias Clasen, and fvwm2 team
* 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.
*/
#include "config.h"
#include <string.h>
#include <stdlib.h>
#include <clutter/clutter.h>
#include <X11/Xatom.h>
#include <X11/extensions/Xrandr.h>
#include <X11/extensions/dpms.h>
#include <meta/main.h>
#include <meta/errors.h>
#include "monitor-private.h"
#define ALL_WL_TRANSFORMS ((1 << (WL_OUTPUT_TRANSFORM_FLIPPED_270 + 1)) - 1)
struct _MetaMonitorManagerXrandr
{
MetaMonitorManager parent_instance;
Display *xdisplay;
XRRScreenResources *resources;
int time;
int rr_event_base;
int rr_error_base;
};
struct _MetaMonitorManagerXrandrClass
{
MetaMonitorManagerClass parent_class;
};
G_DEFINE_TYPE (MetaMonitorManagerXrandr, meta_monitor_manager_xrandr, META_TYPE_MONITOR_MANAGER);
static enum wl_output_transform
wl_transform_from_xrandr (Rotation rotation)
{
static const enum wl_output_transform y_reflected_map[4] = {
WL_OUTPUT_TRANSFORM_FLIPPED_180,
WL_OUTPUT_TRANSFORM_FLIPPED_90,
WL_OUTPUT_TRANSFORM_FLIPPED,
WL_OUTPUT_TRANSFORM_FLIPPED_270
};
enum wl_output_transform ret;
switch (rotation & 0x7F)
{
default:
case RR_Rotate_0:
ret = WL_OUTPUT_TRANSFORM_NORMAL;
break;
case RR_Rotate_90:
ret = WL_OUTPUT_TRANSFORM_90;
break;
case RR_Rotate_180:
ret = WL_OUTPUT_TRANSFORM_180;
break;
case RR_Rotate_270:
ret = WL_OUTPUT_TRANSFORM_270;
break;
}
if (rotation & RR_Reflect_X)
return ret + 4;
else if (rotation & RR_Reflect_Y)
return y_reflected_map[ret];
else
return ret;
}
#define ALL_ROTATIONS (RR_Rotate_0 | RR_Rotate_90 | RR_Rotate_180 | RR_Rotate_270)
static unsigned int
wl_transform_from_xrandr_all (Rotation rotation)
{
unsigned ret;
/* Handle the common cases first (none or all) */
if (rotation == 0 || rotation == RR_Rotate_0)
return (1 << WL_OUTPUT_TRANSFORM_NORMAL);
/* All rotations and one reflection -> all of them by composition */
if ((rotation & ALL_ROTATIONS) &&
((rotation & RR_Reflect_X) || (rotation & RR_Reflect_Y)))
return ALL_WL_TRANSFORMS;
ret = 1 << WL_OUTPUT_TRANSFORM_NORMAL;
if (rotation & RR_Rotate_90)
ret |= 1 << WL_OUTPUT_TRANSFORM_90;
if (rotation & RR_Rotate_180)
ret |= 1 << WL_OUTPUT_TRANSFORM_180;
if (rotation & RR_Rotate_270)
ret |= 1 << WL_OUTPUT_TRANSFORM_270;
if (rotation & (RR_Rotate_0 | RR_Reflect_X))
ret |= 1 << WL_OUTPUT_TRANSFORM_FLIPPED;
if (rotation & (RR_Rotate_90 | RR_Reflect_X))
ret |= 1 << WL_OUTPUT_TRANSFORM_FLIPPED_90;
if (rotation & (RR_Rotate_180 | RR_Reflect_X))
ret |= 1 << WL_OUTPUT_TRANSFORM_FLIPPED_180;
if (rotation & (RR_Rotate_270 | RR_Reflect_X))
ret |= 1 << WL_OUTPUT_TRANSFORM_FLIPPED_270;
return ret;
}
static gboolean
output_get_presentation_xrandr (MetaMonitorManagerXrandr *manager_xrandr,
MetaOutput *output)
{
MetaDisplay *display = meta_get_display ();
gboolean value;
Atom actual_type;
int actual_format;
unsigned long nitems, bytes_after;
unsigned char *buffer;
XRRGetOutputProperty (manager_xrandr->xdisplay,
(XID)output->output_id,
display->atom__MUTTER_PRESENTATION_OUTPUT,
0, G_MAXLONG, False, False, XA_CARDINAL,
&actual_type, &actual_format,
&nitems, &bytes_after, &buffer);
if (actual_type != XA_CARDINAL || actual_format != 32 ||
nitems < 1)
return FALSE;
value = ((int*)buffer)[0];
XFree (buffer);
return value;
}
static int
normalize_backlight (MetaOutput *output,
int hw_value)
{
return round((double)(hw_value - output->backlight_min) /
(output->backlight_max - output->backlight_min) * 100.0);
}
static int
output_get_backlight_xrandr (MetaMonitorManagerXrandr *manager_xrandr,
MetaOutput *output)
{
MetaDisplay *display = meta_get_display ();
gboolean value;
Atom actual_type;
int actual_format;
unsigned long nitems, bytes_after;
unsigned char *buffer;
XRRGetOutputProperty (manager_xrandr->xdisplay,
(XID)output->output_id,
display->atom_BACKLIGHT,
0, G_MAXLONG, False, False, XA_INTEGER,
&actual_type, &actual_format,
&nitems, &bytes_after, &buffer);
if (actual_type != XA_INTEGER || actual_format != 32 ||
nitems < 1)
return -1;
value = ((int*)buffer)[0];
XFree (buffer);
return normalize_backlight (output, value);
}
static void
output_get_backlight_limits_xrandr (MetaMonitorManagerXrandr *manager_xrandr,
MetaOutput *output)
{
MetaDisplay *display = meta_get_display ();
XRRPropertyInfo *info;
meta_error_trap_push (display);
info = XRRQueryOutputProperty (manager_xrandr->xdisplay,
(XID)output->output_id,
display->atom_BACKLIGHT);
meta_error_trap_pop (display);
if (info == NULL)
{
meta_verbose ("could not get output property for %s\n", output->name);
return;
}
if (!info->range || info->num_values != 2)
{
meta_verbose ("backlight %s was not range\n", output->name);
goto out;
}
output->backlight_min = info->values[0];
output->backlight_max = info->values[1];
out:
XFree (info);
}
static int
compare_outputs (const void *one,
const void *two)
{
const MetaOutput *o_one = one, *o_two = two;
return strcmp (o_one->name, o_two->name);
}
static void
meta_monitor_manager_xrandr_read_current (MetaMonitorManager *manager)
{
MetaMonitorManagerXrandr *manager_xrandr = META_MONITOR_MANAGER_XRANDR (manager);
XRRScreenResources *resources;
RROutput primary_output;
unsigned int i, j, k;
unsigned int n_actual_outputs;
int min_width, min_height;
Screen *screen;
BOOL dpms_capable, dpms_enabled;
CARD16 dpms_state;
if (manager_xrandr->resources)
XRRFreeScreenResources (manager_xrandr->resources);
manager_xrandr->resources = NULL;
meta_error_trap_push (meta_get_display ());
dpms_capable = DPMSCapable (manager_xrandr->xdisplay);
meta_error_trap_pop (meta_get_display ());
if (dpms_capable &&
DPMSInfo (manager_xrandr->xdisplay, &dpms_state, &dpms_enabled) &&
dpms_enabled)
{
switch (dpms_state)
{
case DPMSModeOn:
manager->power_save_mode = META_POWER_SAVE_ON;
case DPMSModeStandby:
manager->power_save_mode = META_POWER_SAVE_STANDBY;
case DPMSModeSuspend:
manager->power_save_mode = META_POWER_SAVE_SUSPEND;
case DPMSModeOff:
manager->power_save_mode = META_POWER_SAVE_OFF;
default:
manager->power_save_mode = META_POWER_SAVE_UNKNOWN;
}
}
else
{
manager->power_save_mode = META_POWER_SAVE_UNKNOWN;
}
XRRGetScreenSizeRange (manager_xrandr->xdisplay, DefaultRootWindow (manager_xrandr->xdisplay),
&min_width,
&min_height,
&manager->max_screen_width,
&manager->max_screen_height);
screen = ScreenOfDisplay (manager_xrandr->xdisplay,
DefaultScreen (manager_xrandr->xdisplay));
/* This is updated because we called RRUpdateConfiguration below */
manager->screen_width = WidthOfScreen (screen);
manager->screen_height = HeightOfScreen (screen);
resources = XRRGetScreenResourcesCurrent (manager_xrandr->xdisplay,
DefaultRootWindow (manager_xrandr->xdisplay));
if (!resources)
return;
manager_xrandr->resources = resources;
manager_xrandr->time = resources->configTimestamp;
manager->n_outputs = resources->noutput;
manager->n_crtcs = resources->ncrtc;
manager->n_modes = resources->nmode;
manager->outputs = g_new0 (MetaOutput, manager->n_outputs);
manager->modes = g_new0 (MetaMonitorMode, manager->n_modes);
manager->crtcs = g_new0 (MetaCRTC, manager->n_crtcs);
for (i = 0; i < (unsigned)resources->nmode; i++)
{
XRRModeInfo *xmode = &resources->modes[i];
MetaMonitorMode *mode;
mode = &manager->modes[i];
mode->mode_id = xmode->id;
mode->width = xmode->width;
mode->height = xmode->height;
mode->refresh_rate = (xmode->dotClock /
((float)xmode->hTotal * xmode->vTotal));
}
for (i = 0; i < (unsigned)resources->ncrtc; i++)
{
XRRCrtcInfo *crtc;
MetaCRTC *meta_crtc;
crtc = XRRGetCrtcInfo (manager_xrandr->xdisplay, resources, resources->crtcs[i]);
meta_crtc = &manager->crtcs[i];
meta_crtc->crtc_id = resources->crtcs[i];
meta_crtc->rect.x = crtc->x;
meta_crtc->rect.y = crtc->y;
meta_crtc->rect.width = crtc->width;
meta_crtc->rect.height = crtc->height;
meta_crtc->dirty = FALSE;
meta_crtc->transform = wl_transform_from_xrandr (crtc->rotation);
meta_crtc->all_transforms = wl_transform_from_xrandr_all (crtc->rotations);
for (j = 0; j < (unsigned)resources->nmode; j++)
{
if (resources->modes[j].id == crtc->mode)
{
meta_crtc->current_mode = &manager->modes[j];
break;
}
}
XRRFreeCrtcInfo (crtc);
}
primary_output = XRRGetOutputPrimary (manager_xrandr->xdisplay,
DefaultRootWindow (manager_xrandr->xdisplay));
n_actual_outputs = 0;
for (i = 0; i < (unsigned)resources->noutput; i++)
{
XRROutputInfo *output;
MetaOutput *meta_output;
output = XRRGetOutputInfo (manager_xrandr->xdisplay, resources, resources->outputs[i]);
meta_output = &manager->outputs[n_actual_outputs];
if (output->connection != RR_Disconnected)
{
meta_output->output_id = resources->outputs[i];
meta_output->name = g_strdup (output->name);
meta_output->vendor = g_strdup ("unknown");
meta_output->product = g_strdup ("unknown");
meta_output->serial = g_strdup ("");
meta_output->width_mm = output->mm_width;
meta_output->height_mm = output->mm_height;
meta_output->subpixel_order = COGL_SUBPIXEL_ORDER_UNKNOWN;
meta_output->n_modes = output->nmode;
meta_output->modes = g_new0 (MetaMonitorMode *, meta_output->n_modes);
for (j = 0; j < meta_output->n_modes; j++)
{
for (k = 0; k < manager->n_modes; k++)
{
if (output->modes[j] == (XID)manager->modes[k].mode_id)
{
meta_output->modes[j] = &manager->modes[k];
break;
}
}
}
meta_output->preferred_mode = meta_output->modes[0];
meta_output->n_possible_crtcs = output->ncrtc;
meta_output->possible_crtcs = g_new0 (MetaCRTC *, meta_output->n_possible_crtcs);
for (j = 0; j < (unsigned)output->ncrtc; j++)
{
for (k = 0; k < manager->n_crtcs; k++)
{
if ((XID)manager->crtcs[k].crtc_id == output->crtcs[j])
{
meta_output->possible_crtcs[j] = &manager->crtcs[k];
break;
}
}
}
meta_output->crtc = NULL;
for (j = 0; j < manager->n_crtcs; j++)
{
if ((XID)manager->crtcs[j].crtc_id == output->crtc)
{
meta_output->crtc = &manager->crtcs[j];
break;
}
}
meta_output->n_possible_clones = output->nclone;
meta_output->possible_clones = g_new0 (MetaOutput *, meta_output->n_possible_clones);
/* We can build the list of clones now, because we don't have the list of outputs
yet, so temporarily set the pointers to the bare XIDs, and then we'll fix them
in a second pass
*/
for (j = 0; j < (unsigned)output->nclone; j++)
{
meta_output->possible_clones = GINT_TO_POINTER (output->clones[j]);
}
meta_output->is_primary = ((XID)meta_output->output_id == primary_output);
meta_output->is_presentation = output_get_presentation_xrandr (manager_xrandr, meta_output);
output_get_backlight_limits_xrandr (manager_xrandr, meta_output);
if (!(meta_output->backlight_min == 0 && meta_output->backlight_max == 0))
meta_output->backlight = output_get_backlight_xrandr (manager_xrandr, meta_output);
else
meta_output->backlight = -1;
n_actual_outputs++;
}
XRRFreeOutputInfo (output);
}
manager->n_outputs = n_actual_outputs;
/* Sort the outputs for easier handling in MetaMonitorConfig */
qsort (manager->outputs, manager->n_outputs, sizeof (MetaOutput), compare_outputs);
/* Now fix the clones */
for (i = 0; i < manager->n_outputs; i++)
{
MetaOutput *meta_output;
meta_output = &manager->outputs[i];
for (j = 0; j < meta_output->n_possible_clones; j++)
{
RROutput clone = GPOINTER_TO_INT (meta_output->possible_clones[j]);
for (k = 0; k < manager->n_outputs; k++)
{
if (clone == (XID)manager->outputs[k].output_id)
{
meta_output->possible_clones[j] = &manager->outputs[k];
break;
}
}
}
}
}
static guint8 *
get_edid_property (Display *dpy,
RROutput output,
Atom atom,
gsize *len)
{
unsigned char *prop;
int actual_format;
unsigned long nitems, bytes_after;
Atom actual_type;
guint8 *result;
XRRGetOutputProperty (dpy, output, atom,
0, 100, False, False,
AnyPropertyType,
&actual_type, &actual_format,
&nitems, &bytes_after, &prop);
if (actual_type == XA_INTEGER && actual_format == 8)
{
result = g_memdup (prop, nitems);
if (len)
*len = nitems;
}
else
{
result = NULL;
}
XFree (prop);
return result;
}
static GBytes *
meta_monitor_manager_xrandr_read_edid (MetaMonitorManager *manager,
MetaOutput *output)
{
MetaMonitorManagerXrandr *manager_xrandr = META_MONITOR_MANAGER_XRANDR (manager);
Atom edid_atom;
guint8 *result;
gsize len;
edid_atom = XInternAtom (manager_xrandr->xdisplay, "EDID", FALSE);
result = get_edid_property (manager_xrandr->xdisplay, output->output_id, edid_atom, &len);
if (!result)
{
edid_atom = XInternAtom (manager_xrandr->xdisplay, "EDID_DATA", FALSE);
result = get_edid_property (manager_xrandr->xdisplay, output->output_id, edid_atom, &len);
}
if (!result)
{
edid_atom = XInternAtom (manager_xrandr->xdisplay, "XFree86_DDC_EDID1_RAWDATA", FALSE);
result = get_edid_property (manager_xrandr->xdisplay, output->output_id, edid_atom, &len);
}
if (result)
{
if (len > 0 && len % 128 == 0)
return g_bytes_new_take (result, len);
else
g_free (result);
}
return NULL;
}
static void
meta_monitor_manager_xrandr_set_power_save_mode (MetaMonitorManager *manager,
MetaPowerSave mode)
{
MetaMonitorManagerXrandr *manager_xrandr = META_MONITOR_MANAGER_XRANDR (manager);
CARD16 state;
switch (mode) {
case META_POWER_SAVE_ON:
state = DPMSModeOn;
break;
case META_POWER_SAVE_STANDBY:
state = DPMSModeStandby;
break;
case META_POWER_SAVE_SUSPEND:
state = DPMSModeSuspend;
break;
case META_POWER_SAVE_OFF:
state = DPMSModeOff;
break;
default:
return;
}
meta_error_trap_push (meta_get_display ());
DPMSForceLevel (manager_xrandr->xdisplay, state);
DPMSSetTimeouts (manager_xrandr->xdisplay, 0, 0, 0);
meta_error_trap_pop (meta_get_display ());
}
static Rotation
wl_transform_to_xrandr (enum wl_output_transform transform)
{
switch (transform)
{
case WL_OUTPUT_TRANSFORM_NORMAL:
return RR_Rotate_0;
case WL_OUTPUT_TRANSFORM_90:
return RR_Rotate_90;
case WL_OUTPUT_TRANSFORM_180:
return RR_Rotate_180;
case WL_OUTPUT_TRANSFORM_270:
return RR_Rotate_270;
case WL_OUTPUT_TRANSFORM_FLIPPED:
return RR_Reflect_X | RR_Rotate_0;
case WL_OUTPUT_TRANSFORM_FLIPPED_90:
return RR_Reflect_X | RR_Rotate_90;
case WL_OUTPUT_TRANSFORM_FLIPPED_180:
return RR_Reflect_X | RR_Rotate_180;
case WL_OUTPUT_TRANSFORM_FLIPPED_270:
return RR_Reflect_X | RR_Rotate_270;
}
g_assert_not_reached ();
}
static void
output_set_presentation_xrandr (MetaMonitorManagerXrandr *manager_xrandr,
MetaOutput *output,
gboolean presentation)
{
MetaDisplay *display = meta_get_display ();
int value = presentation;
XRRChangeOutputProperty (manager_xrandr->xdisplay,
(XID)output->output_id,
display->atom__MUTTER_PRESENTATION_OUTPUT,
XA_CARDINAL, 32, PropModeReplace,
(unsigned char*) &value, 1);
}
static void
meta_monitor_manager_xrandr_apply_configuration (MetaMonitorManager *manager,
MetaCRTCInfo **crtcs,
unsigned int n_crtcs,
MetaOutputInfo **outputs,
unsigned int n_outputs)
{
MetaMonitorManagerXrandr *manager_xrandr = META_MONITOR_MANAGER_XRANDR (manager);
unsigned i;
meta_display_grab (meta_get_display ());
for (i = 0; i < n_crtcs; i++)
{
MetaCRTCInfo *crtc_info = crtcs[i];
MetaCRTC *crtc = crtc_info->crtc;
crtc->dirty = TRUE;
if (crtc_info->mode == NULL)
{
XRRSetCrtcConfig (manager_xrandr->xdisplay,
manager_xrandr->resources,
(XID)crtc->crtc_id,
manager_xrandr->time,
0, 0,
None,
RR_Rotate_0,
NULL, 0);
}
else
{
MetaMonitorMode *mode;
XID *outputs;
int j, n_outputs;
Status ok;
mode = crtc_info->mode;
n_outputs = crtc_info->outputs->len;
outputs = g_new (XID, n_outputs);
for (j = 0; j < n_outputs; j++)
outputs[j] = ((MetaOutput**)crtc_info->outputs->pdata)[j]->output_id;
meta_error_trap_push (meta_get_display ());
ok = XRRSetCrtcConfig (manager_xrandr->xdisplay,
manager_xrandr->resources,
(XID)crtc->crtc_id,
manager_xrandr->time,
crtc_info->x, crtc_info->y,
(XID)mode->mode_id,
wl_transform_to_xrandr (crtc_info->transform),
outputs, n_outputs);
meta_error_trap_pop (meta_get_display ());
if (ok != Success)
meta_warning ("Configuring CRTC %d with mode %d (%d x %d @ %f) at position %d, %d and transfrom %u failed\n",
(unsigned)(crtc - manager->crtcs), (unsigned)(mode - manager->modes),
mode->width, mode->height, (float)mode->refresh_rate,
crtc_info->x, crtc_info->y, crtc_info->transform);
g_free (outputs);
}
}
for (i = 0; i < n_outputs; i++)
{
MetaOutputInfo *output_info = outputs[i];
if (output_info->is_primary)
{
XRRSetOutputPrimary (manager_xrandr->xdisplay,
DefaultRootWindow (manager_xrandr->xdisplay),
(XID)output_info->output->output_id);
}
output_set_presentation_xrandr (manager_xrandr,
output_info->output,
output_info->is_presentation);
}
/* Disable CRTCs not mentioned in the list */
for (i = 0; i < manager->n_crtcs; i++)
{
MetaCRTC *crtc = &manager->crtcs[i];
if (crtc->dirty)
{
crtc->dirty = FALSE;
continue;
}
if (crtc->current_mode == NULL)
continue;
XRRSetCrtcConfig (manager_xrandr->xdisplay,
manager_xrandr->resources,
(XID)crtc->crtc_id,
manager_xrandr->time,
0, 0,
None,
RR_Rotate_0,
NULL, 0);
}
meta_display_ungrab (meta_get_display ());
}
static void
meta_monitor_manager_xrandr_change_backlight (MetaMonitorManager *manager,
MetaOutput *output,
gint value)
{
MetaMonitorManagerXrandr *manager_xrandr = META_MONITOR_MANAGER_XRANDR (manager);
MetaDisplay *display = meta_get_display ();
int hw_value;
hw_value = round((double)value / 100.0 * output->backlight_max + output->backlight_min);
meta_error_trap_push (display);
XRRChangeOutputProperty (manager_xrandr->xdisplay,
(XID)output->output_id,
display->atom_BACKLIGHT,
XA_INTEGER, 32, PropModeReplace,
(unsigned char *) &hw_value, 1);
meta_error_trap_pop (display);
/* We're not selecting for property notifies, so update the value immediately */
output->backlight = normalize_backlight (output, hw_value);
}
static void
meta_monitor_manager_xrandr_get_crtc_gamma (MetaMonitorManager *manager,
MetaCRTC *crtc,
gsize *size,
unsigned short **red,
unsigned short **green,
unsigned short **blue)
{
MetaMonitorManagerXrandr *manager_xrandr = META_MONITOR_MANAGER_XRANDR (manager);
XRRCrtcGamma *gamma;
gamma = XRRGetCrtcGamma (manager_xrandr->xdisplay, (XID)crtc->crtc_id);
*size = gamma->size;
*red = g_memdup (gamma->red, sizeof (unsigned short) * gamma->size);
*green = g_memdup (gamma->green, sizeof (unsigned short) * gamma->size);
*blue = g_memdup (gamma->blue, sizeof (unsigned short) * gamma->size);
XRRFreeGamma (gamma);
}
static void
meta_monitor_manager_xrandr_set_crtc_gamma (MetaMonitorManager *manager,
MetaCRTC *crtc,
gsize size,
unsigned short *red,
unsigned short *green,
unsigned short *blue)
{
MetaMonitorManagerXrandr *manager_xrandr = META_MONITOR_MANAGER_XRANDR (manager);
XRRCrtcGamma gamma;
gamma.size = size;
gamma.red = red;
gamma.green = green;
gamma.blue = blue;
XRRSetCrtcGamma (manager_xrandr->xdisplay, (XID)crtc->crtc_id, &gamma);
}
static gboolean
meta_monitor_manager_xrandr_handle_xevent (MetaMonitorManager *manager,
XEvent *event)
{
MetaMonitorManagerXrandr *manager_xrandr = META_MONITOR_MANAGER_XRANDR (manager);
if ((event->type - manager_xrandr->rr_event_base) != RRScreenChangeNotify)
return FALSE;
XRRUpdateConfiguration (event);
return TRUE;
}
static void
meta_monitor_manager_xrandr_init (MetaMonitorManagerXrandr *manager_xrandr)
{
MetaDisplay *display = meta_get_display ();
manager_xrandr->xdisplay = display->xdisplay;
if (!XRRQueryExtension (manager_xrandr->xdisplay,
&manager_xrandr->rr_event_base,
&manager_xrandr->rr_error_base))
{
return;
}
else
{
/* We only use ScreenChangeNotify, but GDK uses the others,
and we don't want to step on its toes */
XRRSelectInput (manager_xrandr->xdisplay,
DefaultRootWindow (manager_xrandr->xdisplay),
RRScreenChangeNotifyMask
| RRCrtcChangeNotifyMask
| RROutputPropertyNotifyMask);
}
}
static void
meta_monitor_manager_xrandr_finalize (GObject *object)
{
MetaMonitorManagerXrandr *manager_xrandr = META_MONITOR_MANAGER_XRANDR (object);
if (manager_xrandr->resources)
XRRFreeScreenResources (manager_xrandr->resources);
manager_xrandr->resources = NULL;
G_OBJECT_CLASS (meta_monitor_manager_xrandr_parent_class)->finalize (object);
}
static void
meta_monitor_manager_xrandr_class_init (MetaMonitorManagerXrandrClass *klass)
{
MetaMonitorManagerClass *manager_class = META_MONITOR_MANAGER_CLASS (klass);
GObjectClass *object_class = G_OBJECT_CLASS (klass);
object_class->finalize = meta_monitor_manager_xrandr_finalize;
manager_class->read_current = meta_monitor_manager_xrandr_read_current;
manager_class->read_edid = meta_monitor_manager_xrandr_read_edid;
manager_class->apply_configuration = meta_monitor_manager_xrandr_apply_configuration;
manager_class->set_power_save_mode = meta_monitor_manager_xrandr_set_power_save_mode;
manager_class->change_backlight = meta_monitor_manager_xrandr_change_backlight;
manager_class->get_crtc_gamma = meta_monitor_manager_xrandr_get_crtc_gamma;
manager_class->set_crtc_gamma = meta_monitor_manager_xrandr_set_crtc_gamma;
manager_class->handle_xevent = meta_monitor_manager_xrandr_handle_xevent;
}

1554
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

@ -55,6 +55,7 @@
#define KEY_GNOME_ANIMATIONS "enable-animations"
#define KEY_GNOME_CURSOR_THEME "cursor-theme"
#define KEY_GNOME_CURSOR_SIZE "cursor-size"
#define KEY_XKB_OPTIONS "xkb-options"
#define KEY_OVERLAY_KEY "overlay-key"
#define KEY_WORKSPACES_ONLY_ON_PRIMARY "workspaces-only-on-primary"
@ -65,6 +66,7 @@
#define SCHEMA_GENERAL "org.gnome.desktop.wm.preferences"
#define SCHEMA_MUTTER "org.gnome.mutter"
#define SCHEMA_INTERFACE "org.gnome.desktop.interface"
#define SCHEMA_INPUT_SOURCES "org.gnome.desktop.input-sources"
#define SETTINGS(s) g_hash_table_lookup (settings_schemas, (s))
@ -87,7 +89,6 @@ static GDesktopTitlebarAction action_double_click_titlebar = G_DESKTOP_TITLEBAR_
static GDesktopTitlebarAction action_middle_click_titlebar = G_DESKTOP_TITLEBAR_ACTION_LOWER;
static GDesktopTitlebarAction action_right_click_titlebar = G_DESKTOP_TITLEBAR_ACTION_MENU;
static gboolean dynamic_workspaces = FALSE;
static gboolean application_based = FALSE;
static gboolean disable_workarounds = FALSE;
static gboolean auto_raise = FALSE;
static gboolean auto_raise_delay = 500;
@ -115,6 +116,7 @@ static gboolean workspaces_only_on_primary = FALSE;
static gboolean no_tab_popup = FALSE;
static char *iso_next_group_option = NULL;
static void handle_preference_update_enum (GSettings *settings,
gchar *key);
@ -122,7 +124,6 @@ static gboolean update_binding (MetaKeyPref *binding,
gchar **strokes);
static gboolean update_key_binding (const char *key,
gchar **strokes);
static gboolean update_workspace_names (void);
static void settings_changed (GSettings *settings,
gchar *key,
@ -140,11 +141,11 @@ static gboolean theme_name_handler (GVariant*, gpointer*, gpointer);
static gboolean mouse_button_mods_handler (GVariant*, gpointer*, gpointer);
static gboolean button_layout_handler (GVariant*, gpointer*, gpointer);
static gboolean overlay_key_handler (GVariant*, gpointer*, gpointer);
static gboolean iso_next_group_handler (GVariant*, gpointer*, gpointer);
static void do_override (char *key, char *schema);
static void init_bindings (void);
static void init_workspace_names (void);
typedef struct
@ -198,6 +199,13 @@ typedef struct
gchar **target;
} MetaStringPreference;
typedef struct
{
MetaBasePreference base;
GSettingsGetMapping handler;
gchar ***target;
} MetaStringArrayPreference;
typedef struct
{
MetaBasePreference base;
@ -289,13 +297,6 @@ static MetaBoolPreference preferences_bool[] =
},
&dynamic_workspaces,
},
{
{ "application-based",
SCHEMA_GENERAL,
META_PREF_APPLICATION_BASED,
},
NULL, /* feature is known but disabled */
},
{
{ "disable-workarounds",
SCHEMA_GENERAL,
@ -436,6 +437,27 @@ static MetaStringPreference preferences_string[] =
{ { NULL, 0, 0 }, NULL },
};
static MetaStringArrayPreference preferences_string_array[] =
{
{
{ KEY_WORKSPACE_NAMES,
SCHEMA_GENERAL,
META_PREF_KEYBINDINGS,
},
NULL,
&workspace_names,
},
{
{ KEY_XKB_OPTIONS,
SCHEMA_INPUT_SOURCES,
META_PREF_KEYBINDINGS,
},
iso_next_group_handler,
NULL,
},
{ { NULL, 0, 0 }, NULL },
};
static MetaIntPreference preferences_int[] =
{
{
@ -555,6 +577,42 @@ handle_preference_init_string (void)
}
}
static void
handle_preference_init_string_array (void)
{
MetaStringArrayPreference *cursor = preferences_string_array;
while (cursor->base.key != NULL)
{
char **value;
/* Complex keys have a mapping function to check validity */
if (cursor->handler)
{
if (cursor->target)
meta_bug ("%s has both a target and a handler\n", cursor->base.key);
g_settings_get_mapped (SETTINGS (cursor->base.schema),
cursor->base.key, cursor->handler, NULL);
}
else
{
if (!cursor->target)
meta_bug ("%s must have handler or target\n", cursor->base.key);
if (*(cursor->target))
g_strfreev (*(cursor->target));
value = g_settings_get_strv (SETTINGS (cursor->base.schema),
cursor->base.key);
*(cursor->target) = value;
}
++cursor;
}
}
static void
handle_preference_init_int (void)
{
@ -673,6 +731,56 @@ handle_preference_update_string (GSettings *settings,
queue_changed (cursor->base.pref);
}
static void
handle_preference_update_string_array (GSettings *settings,
gchar *key)
{
MetaStringArrayPreference *cursor = preferences_string_array;
gboolean inform_listeners = FALSE;
while (cursor->base.key != NULL && strcmp (key, cursor->base.key) != 0)
++cursor;
if (cursor->base.key==NULL)
/* Didn't recognise that key. */
return;
/* Complex keys have a mapping function to check validity */
if (cursor->handler)
{
if (cursor->target)
meta_bug ("%s has both a target and a handler\n", cursor->base.key);
g_settings_get_mapped (SETTINGS (cursor->base.schema),
cursor->base.key, cursor->handler, NULL);
}
else
{
char **values, **previous;
int n_values, n_previous, i;
if (!cursor->target)
meta_bug ("%s must have handler or target\n", cursor->base.key);
values = g_settings_get_strv (SETTINGS (cursor->base.schema),
cursor->base.key);
n_values = g_strv_length (values);
previous = *(cursor->target);
n_previous = previous ? g_strv_length (previous) : 0;
inform_listeners = n_previous != n_values;
for (i = 0; i < n_values && !inform_listeners; i++)
inform_listeners = g_strcmp0 (values[i], previous[i]) != 0;
if (*(cursor->target))
g_strfreev (*(cursor->target));
*(cursor->target) = values;
}
if (inform_listeners)
queue_changed (cursor->base.pref);
}
static void
handle_preference_update_int (GSettings *settings,
gchar *key)
@ -857,6 +965,11 @@ meta_prefs_init (void)
G_CALLBACK (settings_changed), NULL);
g_hash_table_insert (settings_schemas, g_strdup (SCHEMA_INTERFACE), settings);
settings = g_settings_new (SCHEMA_INPUT_SOURCES);
g_signal_connect (settings, "changed::" KEY_XKB_OPTIONS,
G_CALLBACK (settings_changed), NULL);
g_hash_table_insert (settings_schemas, g_strdup (SCHEMA_INPUT_SOURCES), settings);
for (tmp = overridden_keys; tmp; tmp = tmp->next)
{
@ -869,10 +982,10 @@ meta_prefs_init (void)
handle_preference_init_enum ();
handle_preference_init_bool ();
handle_preference_init_string ();
handle_preference_init_string_array ();
handle_preference_init_int ();
init_bindings ();
init_workspace_names ();
}
static gboolean
@ -1020,14 +1133,6 @@ settings_changed (GSettings *settings,
MetaEnumPreference *cursor;
gboolean found_enum;
/* String array, handled separately */
if (strcmp (key, KEY_WORKSPACE_NAMES) == 0)
{
if (update_workspace_names ())
queue_changed (META_PREF_WORKSPACE_NAMES);
return;
}
value = g_settings_get_value (settings, key);
type = g_variant_get_type (value);
@ -1035,6 +1140,8 @@ settings_changed (GSettings *settings,
handle_preference_update_bool (settings, key);
else if (g_variant_type_equal (type, G_VARIANT_TYPE_INT32))
handle_preference_update_int (settings, key);
else if (g_variant_type_equal (type, G_VARIANT_TYPE_STRING_ARRAY))
handle_preference_update_string_array (settings, key);
else if (g_variant_type_equal (type, G_VARIANT_TYPE_STRING))
{
cursor = preferences_enum;
@ -1554,6 +1661,39 @@ overlay_key_handler (GVariant *value,
return TRUE;
}
static gboolean
iso_next_group_handler (GVariant *value,
gpointer *result,
gpointer data)
{
const char **xkb_options, **p;
const char *option = NULL;
gboolean changed = FALSE;
*result = NULL; /* ignored */
xkb_options = g_variant_get_strv (value, NULL);
for (p = xkb_options; p && *p; ++p)
if (g_str_has_prefix (*p, "grp:"))
{
option = (*p + 4);
break;
}
changed = (g_strcmp0 (option, iso_next_group_option) != 0);
if (changed)
{
g_free (iso_next_group_option);
iso_next_group_option = g_strdup (option);
queue_changed (META_PREF_KEYBINDINGS);
}
g_free (xkb_options);
return TRUE;
}
const PangoFontDescription*
meta_prefs_get_titlebar_font (void)
{
@ -1575,14 +1715,6 @@ meta_prefs_get_dynamic_workspaces (void)
return dynamic_workspaces;
}
gboolean
meta_prefs_get_application_based (void)
{
return FALSE; /* For now, we never want this to do anything */
return application_based;
}
gboolean
meta_prefs_get_disable_workarounds (void)
{
@ -1620,9 +1752,6 @@ meta_preference_to_string (MetaPreference pref)
case META_PREF_NUM_WORKSPACES:
return "NUM_WORKSPACES";
case META_PREF_APPLICATION_BASED:
return "APPLICATION_BASED";
case META_PREF_KEYBINDINGS:
return "KEYBINDINGS";
@ -1708,12 +1837,13 @@ meta_prefs_set_num_workspaces (int n_workspaces)
{
MetaBasePreference *pref;
find_pref (preferences_int, sizeof(MetaIntPreference),
KEY_NUM_WORKSPACES, &pref);
g_settings_set_int (SETTINGS (pref->schema),
KEY_NUM_WORKSPACES,
n_workspaces);
if (find_pref (preferences_int, sizeof(MetaIntPreference),
KEY_NUM_WORKSPACES, &pref))
{
g_settings_set_int (SETTINGS (pref->schema),
KEY_NUM_WORKSPACES,
n_workspaces);
}
}
static GHashTable *key_bindings;
@ -1747,20 +1877,15 @@ init_bindings (void)
g_hash_table_insert (key_bindings, g_strdup ("overlay-key"), pref);
}
static void
init_workspace_names (void)
{
update_workspace_names ();
}
static gboolean
update_binding (MetaKeyPref *binding,
gchar **strokes)
{
GSList *old_bindings, *a, *b;
gboolean changed;
unsigned int keysym;
unsigned int keycode;
MetaVirtualModifier mods;
gboolean changed = FALSE;
MetaKeyCombo *combo;
int i;
@ -1768,13 +1893,9 @@ update_binding (MetaKeyPref *binding,
"Binding \"%s\" has new GSettings value\n",
binding->name);
/* Okay, so, we're about to provide a new list of key combos for this
* action. Delete any pre-existing list.
*/
g_slist_foreach (binding->bindings, (GFunc) g_free, NULL);
g_slist_free (binding->bindings);
old_bindings = binding->bindings;
binding->bindings = NULL;
for (i = 0; strokes && strokes[i]; i++)
{
keysym = 0;
@ -1809,8 +1930,6 @@ update_binding (MetaKeyPref *binding,
* Changing the key in response to a modification could lead to cyclic calls. */
continue;
}
changed = TRUE;
combo = g_malloc0 (sizeof (MetaKeyCombo));
combo->keysym = keysym;
@ -1825,6 +1944,34 @@ update_binding (MetaKeyPref *binding,
binding->bindings = g_slist_reverse (binding->bindings);
a = old_bindings;
b = binding->bindings;
while (TRUE)
{
if ((!a && b) || (a && !b))
{
changed = TRUE;
break;
}
else if (!a && !b)
{
changed = FALSE;
break;
}
else if (memcmp (a->data, b->data, sizeof (MetaKeyCombo)) != 0)
{
changed = TRUE;
break;
}
else
{
a = a->next;
b = b->next;
}
}
g_slist_free_full (old_bindings, g_free);
return changed;
}
@ -1840,41 +1987,6 @@ update_key_binding (const char *key,
return FALSE;
}
static gboolean
update_workspace_names (void)
{
int i;
char **names;
int n_workspace_names, n_names;
gboolean changed = FALSE;
names = g_settings_get_strv (SETTINGS (SCHEMA_GENERAL), KEY_WORKSPACE_NAMES);
n_names = g_strv_length (names);
n_workspace_names = workspace_names ? g_strv_length (workspace_names) : 0;
for (i = 0; i < n_names; i++)
if (n_workspace_names < i + 1 || !workspace_names[i] ||
g_strcmp0 (names[i], workspace_names[i]) != 0)
{
changed = TRUE;
break;
}
if (n_workspace_names != n_names)
changed = TRUE;
if (changed)
{
if (workspace_names)
g_strfreev (workspace_names);
workspace_names = names;
}
else
g_strfreev (names);
return changed;
}
const char*
meta_prefs_get_workspace_name (int i)
{
@ -2074,6 +2186,12 @@ meta_prefs_get_overlay_binding (MetaKeyCombo *combo)
*combo = overlay_key_combo;
}
const char *
meta_prefs_get_iso_next_group_option (void)
{
return iso_next_group_option;
}
GDesktopTitlebarAction
meta_prefs_get_action_double_click_titlebar (void)
{
@ -2216,9 +2334,11 @@ meta_prefs_set_no_tab_popup (gboolean whether)
{
MetaBasePreference *pref;
find_pref (preferences_bool, sizeof(MetaBoolPreference),
KEY_NO_TAB_POPUP, &pref);
g_settings_set_boolean (SETTINGS (pref->schema), KEY_NO_TAB_POPUP, whether);
if (find_pref (preferences_bool, sizeof(MetaBoolPreference),
KEY_NO_TAB_POPUP, &pref))
{
g_settings_set_boolean (SETTINGS (pref->schema), KEY_NO_TAB_POPUP, whether);
}
}
int

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;
@ -187,6 +179,9 @@ MetaWindow* meta_screen_get_mouse_window (MetaScreen *scre
MetaWindow *not_this_one);
const MetaMonitorInfo* meta_screen_get_current_monitor_info (MetaScreen *screen);
const MetaMonitorInfo* meta_screen_get_current_monitor_info_for_pos (MetaScreen *screen,
int x,
int y);
const MetaMonitorInfo* meta_screen_get_monitor_for_rect (MetaScreen *screen,
MetaRectangle *rect);
const MetaMonitorInfo* meta_screen_get_monitor_for_window (MetaScreen *screen,
@ -228,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);
@ -254,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);
int meta_screen_xinerama_index_to_monitor_index (MetaScreen *screen,
int index);
int meta_screen_monitor_index_to_xinerama_index (MetaScreen *screen,
int index);
gboolean meta_screen_handle_xevent (MetaScreen *screen,
XEvent *xevent);
#endif

View File

@ -45,13 +45,14 @@
#include <meta/compositor.h>
#include "mutter-enum-types.h"
#include "core.h"
#ifdef HAVE_WAYLAND
#include "meta-wayland-private.h"
#endif
#include "meta-cursor-tracker-private.h"
#include "meta-idle-monitor-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 +77,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 +354,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 +450,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 +489,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 +518,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 +677,26 @@ 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);
#ifdef HAVE_WAYLAND
if (!meta_is_wayland_compositor ())
#endif
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);
#ifdef HAVE_WAYLAND
if (!meta_is_wayland_compositor ())
#endif
meta_monitor_manager_init_dbus (manager, NULL, NULL);
meta_idle_monitor_init_dbus ();
screen->current_cursor = -1; /* invalid/unset */
screen->default_xvisual = DefaultVisualOfScreen (screen->xscreen);
screen->default_depth = DefaultDepthOfScreen (screen->xscreen);
@ -852,12 +721,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 +807,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 +948,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 +1479,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 +1753,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);
}
@ -2216,6 +2074,65 @@ meta_screen_get_current_monitor_info (MetaScreen *screen)
return &screen->monitor_infos[monitor_index];
}
const MetaMonitorInfo*
meta_screen_get_current_monitor_info_for_pos (MetaScreen *screen,
int x,
int y)
{
int monitor_index;
monitor_index = meta_screen_get_current_monitor_for_pos (screen, x, y);
return &screen->monitor_infos[monitor_index];
}
/**
* meta_screen_get_current_monitor_for_pos:
* @screen: a #MetaScreen
* @x: The x coordinate
* @y: The y coordinate
*
* Gets the index of the monitor that contains the passed coordinates.
*
* Return value: a monitor index
*/
int
meta_screen_get_current_monitor_for_pos (MetaScreen *screen,
int x,
int y)
{
if (screen->n_monitor_infos == 1)
return 0;
else if (screen->display->monitor_cache_invalidated)
{
int i;
MetaRectangle pointer_position;
pointer_position.x = x;
pointer_position.y = y;
pointer_position.width = pointer_position.height = 1;
screen->display->monitor_cache_invalidated = FALSE;
screen->last_monitor_index = 0;
for (i = 0; i < screen->n_monitor_infos; i++)
{
if (meta_rectangle_contains_rect (&screen->monitor_infos[i].rect,
&pointer_position))
{
screen->last_monitor_index = i;
break;
}
}
meta_topic (META_DEBUG_XINERAMA,
"Rechecked current monitor, now %d\n",
screen->last_monitor_index);
}
return screen->last_monitor_index;
}
/**
* meta_screen_get_current_monitor:
* @screen: a #MetaScreen
@ -2241,11 +2158,7 @@ meta_screen_get_current_monitor (MetaScreen *screen)
XIButtonState buttons;
XIModifierState mods;
XIGroupState group;
int i;
MetaRectangle pointer_position;
screen->display->monitor_cache_invalidated = FALSE;
XIQueryPointer (screen->display->xdisplay,
META_VIRTUAL_CORE_POINTER_ID,
screen->xroot,
@ -2260,24 +2173,7 @@ meta_screen_get_current_monitor (MetaScreen *screen)
&group);
free (buttons.mask);
pointer_position.x = root_x_return;
pointer_position.y = root_y_return;
pointer_position.width = pointer_position.height = 1;
screen->last_monitor_index = 0;
for (i = 0; i < screen->n_monitor_infos; i++)
{
if (meta_rectangle_contains_rect (&screen->monitor_infos[i].rect,
&pointer_position))
{
screen->last_monitor_index = i;
break;
}
}
meta_topic (META_DEBUG_XINERAMA,
"Rechecked current monitor, now %d\n",
screen->last_monitor_index);
meta_screen_get_current_monitor_for_pos (screen, root_x_return, root_y_return);
}
return screen->last_monitor_index;
@ -2959,19 +2855,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);
@ -2983,8 +2875,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,
@ -2992,16 +2884,16 @@ meta_screen_resize (MetaScreen *screen,
&changes);
}
if (screen->display->compositor)
meta_compositor_sync_screen_size (screen->display->compositor,
screen, width, height);
meta_compositor_sync_screen_size (screen->display->compositor,
screen,
screen->rect.width, screen->rect.height);
/* Queue a resize on all the windows */
meta_screen_foreach_window (screen, meta_screen_resize_func, 0);
/* Fix up monitor for all windows on this screen */
windows = meta_display_list_windows (screen->display,
META_LIST_DEFAULT);
META_LIST_INCLUDE_OVERRIDE_REDIRECT);
for (tmp = windows; tmp != NULL; tmp = tmp->next)
{
MetaWindow *window = tmp->data;
@ -3010,7 +2902,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);
@ -3806,3 +3697,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);
}
@ -278,7 +295,7 @@ static gboolean
is_focused_foreach (MetaWindow *window,
void *data)
{
if (window == window->display->expected_focus_window)
if (window->has_focus)
{
*((gboolean*) data) = TRUE;
return FALSE;
@ -335,11 +352,11 @@ get_standalone_layer (MetaWindow *window)
layer = META_LAYER_BOTTOM;
else if (window->fullscreen &&
(focused_transient ||
window == window->display->expected_focus_window ||
window->display->expected_focus_window == NULL ||
(window->display->expected_focus_window != NULL &&
window == window->display->focus_window ||
window->display->focus_window == NULL ||
(window->display->focus_window != NULL &&
windows_on_different_monitor (window,
window->display->expected_focus_window))))
window->display->focus_window))))
layer = META_LAYER_FULLSCREEN;
else if (window->wm_state_above && !META_WINDOW_MAXIMIZED (window))
layer = META_LAYER_TOP;
@ -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,17 @@
#include <X11/Xutil.h>
#include <cairo.h>
#include <gdk-pixbuf/gdk-pixbuf.h>
#ifdef HAVE_WAYLAND
#include "meta-wayland-private.h"
#endif
/* 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 +80,7 @@ typedef enum {
_NET_WM_BYPASS_COMPOSITOR_HINT_OFF = 2,
} MetaBypassCompositorHintValue;
struct _MetaWindow
{
GObject parent_instance;
@ -77,6 +89,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 +181,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;
@ -277,7 +293,7 @@ struct _MetaWindow
/* EWHH demands attention flag */
guint wm_state_demands_attention : 1;
/* this flag tracks receipt of focus_in focus_out */
/* TRUE iff window == window->display->focus_window */
guint has_focus : 1;
/* Have we placed this window? */
@ -325,8 +341,10 @@ 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 */
/* has a bounding shape mask */
guint has_shape : 1;
/* has an input shape mask */
guint has_input_shape : 1;
/* icon props have changed */
guint need_reread_icon : 1;
@ -394,6 +412,9 @@ struct _MetaWindow
*/
MetaRectangle rect;
gboolean has_custom_frame_extents;
GtkBorder custom_frame_extents;
/* The geometry to restore when we unmaximize. The position is in
* root window coords, even if there's a frame, which contrasts with
* window->rect above. Note that this gives the position and size
@ -487,6 +508,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);
@ -590,9 +615,8 @@ gboolean meta_window_property_notify (MetaWindow *window,
XEvent *event);
gboolean meta_window_client_message (MetaWindow *window,
XEvent *event);
gboolean meta_window_notify_focus (MetaWindow *window,
XIEnterEvent *event);
void meta_window_lost_focus (MetaWindow *window);
void meta_window_set_focused_internal (MetaWindow *window,
gboolean focused);
void meta_window_set_current_workspace_hint (MetaWindow *window);

View File

@ -289,6 +289,35 @@ reload_icon_geometry (MetaWindow *window,
}
}
static void
reload_gtk_frame_extents (MetaWindow *window,
MetaPropValue *value,
gboolean initial)
{
if (value->type != META_PROP_VALUE_INVALID)
{
if (value->v.cardinal_list.n_cardinals != 4)
{
meta_verbose ("_GTK_FRAME_EXTENTS on %s has %d values instead of 4\n",
window->desc, value->v.cardinal_list.n_cardinals);
}
else
{
GtkBorder *extents = &window->custom_frame_extents;
window->has_custom_frame_extents = TRUE;
extents->left = (int)value->v.cardinal_list.cardinals[0];
extents->right = (int)value->v.cardinal_list.cardinals[1];
extents->top = (int)value->v.cardinal_list.cardinals[2];
extents->bottom = (int)value->v.cardinal_list.cardinals[3];
}
}
else
{
window->has_custom_frame_extents = FALSE;
}
}
static void
reload_struts (MetaWindow *window,
MetaPropValue *value,
@ -1766,6 +1795,7 @@ meta_display_init_window_prop_hooks (MetaDisplay *display)
{ display->atom__GTK_WINDOW_OBJECT_PATH, META_PROP_VALUE_UTF8, reload_gtk_window_object_path, TRUE, FALSE },
{ display->atom__GTK_APP_MENU_OBJECT_PATH, META_PROP_VALUE_UTF8, reload_gtk_app_menu_object_path, TRUE, FALSE },
{ display->atom__GTK_MENUBAR_OBJECT_PATH, META_PROP_VALUE_UTF8, reload_gtk_menubar_object_path, TRUE, FALSE },
{ display->atom__GTK_FRAME_EXTENTS, META_PROP_VALUE_CARDINAL_LIST,reload_gtk_frame_extents, TRUE, FALSE },
{ display->atom__NET_WM_USER_TIME_WINDOW, META_PROP_VALUE_WINDOW, reload_net_wm_user_time_window, TRUE, FALSE },
{ display->atom_WM_STATE, META_PROP_VALUE_INVALID, NULL, FALSE, FALSE },
{ display->atom__NET_WM_ICON, META_PROP_VALUE_INVALID, reload_net_wm_icon, FALSE, FALSE },

File diff suppressed because it is too large Load Diff

View File

@ -54,9 +54,6 @@ item(WM_WINDOW_ROLE)
item(UTF8_STRING)
item(WM_ICON_SIZE)
item(_KWM_WIN_ICON)
item(_MUTTER_RELOAD_THEME_MESSAGE)
item(_MUTTER_SET_KEYBINDINGS_MESSAGE)
item(_MUTTER_TOGGLE_VERBOSE)
item(_MUTTER_HINTS)
item(_GTK_THEME_VARIANT)
item(_GTK_HIDE_TITLEBAR_WHEN_MAXIMIZED)
@ -66,12 +63,16 @@ item(_GTK_APPLICATION_OBJECT_PATH)
item(_GTK_WINDOW_OBJECT_PATH)
item(_GTK_APP_MENU_OBJECT_PATH)
item(_GTK_MENUBAR_OBJECT_PATH)
item(_GTK_FRAME_EXTENTS)
item(_GNOME_WM_KEYBINDINGS)
item(_GNOME_PANEL_ACTION)
item(_GNOME_PANEL_ACTION_MAIN_MENU)
item(_GNOME_PANEL_ACTION_RUN_DIALOG)
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

@ -35,7 +35,6 @@
/* Public compositor API */
ClutterActor *meta_get_stage_for_screen (MetaScreen *screen);
ClutterActor *meta_get_overlay_group_for_screen (MetaScreen *screen);
Window meta_get_overlay_window (MetaScreen *screen);
GList *meta_get_window_actors (MetaScreen *screen);
ClutterActor *meta_get_window_group_for_screen (MetaScreen *screen);
@ -47,5 +46,8 @@ void meta_enable_unredirect_for_screen (MetaScreen *screen);
void meta_set_stage_input_region (MetaScreen *screen,
XserverRegion region);
void meta_empty_stage_input_region (MetaScreen *screen);
void meta_focus_stage_window (MetaScreen *screen,
guint32 timestamp);
gboolean meta_stage_is_focused (MetaScreen *screen);
#endif

View File

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

View File

@ -165,6 +165,10 @@ void meta_display_set_input_focus_window (MetaDisplay *display,
gboolean focus_frame,
guint32 timestamp);
void meta_display_request_take_focus (MetaDisplay *display,
MetaWindow *window,
guint32 timestamp);
/* meta_display_focus_the_no_focus_window is called when the
* designated no_focus_window should be focused, but is otherwise the
* same as meta_display_set_input_focus_window
@ -187,4 +191,11 @@ void meta_display_unmanage_screen (MetaDisplay *display,
void meta_display_clear_mouse_mode (MetaDisplay *display);
void meta_display_freeze_keyboard (MetaDisplay *display,
Window window,
guint32 timestamp);
void meta_display_ungrab_keyboard (MetaDisplay *display,
guint32 timestamp);
void meta_display_unfreeze_keyboard (MetaDisplay *display,
guint32 timestamp);
#endif

View File

@ -0,0 +1,49 @@
/* -*- 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>
#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);
guint64 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

@ -29,6 +29,11 @@
#include <clutter/clutter.h>
#include <X11/Xlib.h>
#ifdef HAVE_WAYLAND
#include <wayland-server.h>
#include "meta-wayland-private.h"
#endif
G_BEGIN_DECLS
#define META_TYPE_SHAPED_TEXTURE (meta_shaped_texture_get_type())
@ -64,7 +69,13 @@ struct _MetaShapedTexture
GType meta_shaped_texture_get_type (void) G_GNUC_CONST;
ClutterActor *meta_shaped_texture_new (void);
ClutterActor *meta_shaped_texture_new_with_xwindow (Window xwindow);
#ifdef HAVE_WAYLAND
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);
#endif
void meta_shaped_texture_set_create_mipmaps (MetaShapedTexture *stex,
gboolean create_mipmaps);
@ -77,11 +88,17 @@ void meta_shaped_texture_update_area (MetaShapedTexture *stex,
void meta_shaped_texture_set_pixmap (MetaShapedTexture *stex,
Pixmap pixmap);
#ifdef HAVE_WAYLAND
void meta_shaped_texture_attach_wayland_buffer (MetaShapedTexture *stex,
MetaWaylandBuffer *buffer);
#endif
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,

View File

@ -64,7 +64,6 @@ gint meta_window_actor_get_workspace (MetaWindowActor *self
MetaWindow * meta_window_actor_get_meta_window (MetaWindowActor *self);
ClutterActor * meta_window_actor_get_texture (MetaWindowActor *self);
gboolean meta_window_actor_is_override_redirect (MetaWindowActor *self);
const char * meta_window_actor_get_description (MetaWindowActor *self);
gboolean meta_window_actor_showing_on_its_workspace (MetaWindowActor *self);
gboolean meta_window_actor_is_destroyed (MetaWindowActor *self);

View File

@ -49,7 +49,6 @@
* @META_PREF_TITLEBAR_FONT: title-bar font
* @META_PREF_NUM_WORKSPACES: number of workspaces
* @META_PREF_DYNAMIC_WORKSPACES: dynamic workspaces
* @META_PREF_APPLICATION_BASED: application-based
* @META_PREF_KEYBINDINGS: keybindings
* @META_PREF_DISABLE_WORKAROUNDS: disable workarounds
* @META_PREF_BUTTON_LAYOUT: button layout
@ -88,7 +87,6 @@ typedef enum
META_PREF_TITLEBAR_FONT,
META_PREF_NUM_WORKSPACES,
META_PREF_DYNAMIC_WORKSPACES,
META_PREF_APPLICATION_BASED,
META_PREF_KEYBINDINGS,
META_PREF_DISABLE_WORKAROUNDS,
META_PREF_BUTTON_LAYOUT,
@ -136,7 +134,6 @@ const char* meta_prefs_get_theme (void);
const PangoFontDescription* meta_prefs_get_titlebar_font (void);
int meta_prefs_get_num_workspaces (void);
gboolean meta_prefs_get_dynamic_workspaces (void);
gboolean meta_prefs_get_application_based (void);
gboolean meta_prefs_get_disable_workarounds (void);
gboolean meta_prefs_get_auto_raise (void);
int meta_prefs_get_auto_raise_delay (void);
@ -353,6 +350,7 @@ typedef enum _MetaKeyBindingAction
META_KEYBINDING_ACTION_MOVE_TO_SIDE_W,
META_KEYBINDING_ACTION_MOVE_TO_CENTER,
META_KEYBINDING_ACTION_OVERLAY_KEY,
META_KEYBINDING_ACTION_ISO_NEXT_GROUP,
META_KEYBINDING_ACTION_LAST
} MetaKeyBindingAction;
@ -442,6 +440,7 @@ void meta_prefs_get_window_binding (const char *name,
MetaVirtualModifier *modifiers);
void meta_prefs_get_overlay_binding (MetaKeyCombo *combo);
const char *meta_prefs_get_iso_next_group_option (void);
gboolean meta_prefs_get_visual_bell (void);
gboolean meta_prefs_bell_is_audible (void);

View File

@ -1,87 +0,0 @@
/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
/* Metacity theme preview widget */
/*
* Copyright (C) 2002 Havoc Pennington
*
* 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 <meta/common.h>
#include <meta/theme.h>
#include <gtk/gtk.h>
#ifndef META_PREVIEW_WIDGET_H
#define META_PREVIEW_WIDGET_H
#define META_TYPE_PREVIEW (meta_preview_get_type ())
#define META_PREVIEW(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), META_TYPE_PREVIEW, MetaPreview))
#define META_PREVIEW_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), META_TYPE_PREVIEW, MetaPreviewClass))
#define META_IS_PREVIEW(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), META_TYPE_PREVIEW))
#define META_IS_PREVIEW_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), META_TYPE_PREVIEW))
#define META_PREVIEW_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), META_TYPE_PREVIEW, MetaPreviewClass))
typedef struct _MetaPreview MetaPreview;
typedef struct _MetaPreviewClass MetaPreviewClass;
struct _MetaPreview
{
GtkBin bin;
GtkStyleContext *style_context;
MetaTheme *theme;
char *title;
MetaFrameType type;
MetaFrameFlags flags;
PangoLayout *layout;
int text_height;
MetaFrameBorders borders;
guint borders_cached : 1;
MetaButtonLayout button_layout;
};
struct _MetaPreviewClass
{
/*< private >*/
GtkBinClass parent_class;
};
GType meta_preview_get_type (void) G_GNUC_CONST;
GtkWidget* meta_preview_new (void);
void meta_preview_set_theme (MetaPreview *preview,
MetaTheme *theme);
void meta_preview_set_title (MetaPreview *preview,
const char *title);
void meta_preview_set_frame_type (MetaPreview *preview,
MetaFrameType type);
void meta_preview_set_frame_flags (MetaPreview *preview,
MetaFrameFlags flags);
void meta_preview_set_button_layout (MetaPreview *preview,
const MetaButtonLayout *button_layout);
GdkPixbuf* meta_preview_get_icon (void);
GdkPixbuf* meta_preview_get_mini_icon (void);
#endif

View File

@ -78,6 +78,9 @@ MetaWorkspace * meta_screen_get_active_workspace (MetaScreen *screen);
int meta_screen_get_n_monitors (MetaScreen *screen);
int meta_screen_get_primary_monitor (MetaScreen *screen);
int meta_screen_get_current_monitor (MetaScreen *screen);
int meta_screen_get_current_monitor_for_pos (MetaScreen *screen,
int x,
int y);
void meta_screen_get_monitor_geometry (MetaScreen *screen,
int monitor,
MetaRectangle *geometry);

View File

@ -33,8 +33,7 @@
typedef struct _MetaTheme MetaTheme;
MetaTheme* meta_theme_get_current (void);
void meta_theme_set_current (const char *name,
gboolean force_reload);
void meta_theme_set_current (const char *name);
MetaTheme* meta_theme_new (void);
void meta_theme_free (MetaTheme *theme);

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))
@ -237,4 +247,6 @@ void meta_window_begin_grab_op (MetaWindow *window,
gboolean frame_action,
guint32 timestamp);
gboolean meta_window_can_close (MetaWindow *window);
#endif

View File

@ -116,5 +116,34 @@
<_summary>Cancel tab popup</_summary>
</key>
<key name="switch-to-session-1" type="as">
<default><![CDATA[['<Primary><Alt>F1']]]></default>
<_summary>Switch to VT 1</_summary>
</key>
<key name="switch-to-session-2" type="as">
<default><![CDATA[['<Primary><Alt>F2']]]></default>
<_summary>Switch to VT 2</_summary>
</key>
<key name="switch-to-session-3" type="as">
<default><![CDATA[['<Primary><Alt>F3']]]></default>
<_summary>Switch to VT 3</_summary>
</key>
<key name="switch-to-session-4" type="as">
<default><![CDATA[['<Primary><Alt>F4']]]></default>
<_summary>Switch to VT 4</_summary>
</key>
<key name="switch-to-session-5" type="as">
<default><![CDATA[['<Primary><Alt>F5']]]></default>
<_summary>Switch to VT 5</_summary>
</key>
<key name="switch-to-session-6" type="as">
<default><![CDATA[['<Primary><Alt>F6']]]></default>
<_summary>Switch to VT 6</_summary>
</key>
<key name="switch-to-session-7" type="as">
<default><![CDATA[['<Primary><Alt>F7']]]></default>
<_summary>Switch to VT 7</_summary>
</key>
</schema>
</schemalist>

Binary file not shown.

Before

Width:  |  Height:  |  Size: 220 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 166 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 145 B

View File

@ -1,8 +0,0 @@
Makefile
Makefile.in
metacity-grayscale
metacity-message
metacity-mag
metacity-properties
metacity-properties.desktop
metacity-window-demo

View File

@ -1,34 +0,0 @@
@INTLTOOL_DESKTOP_RULE@
icondir=$(pkgdatadir)/icons
icon_DATA=mutter-window-demo.png
INCLUDES=@MUTTER_WINDOW_DEMO_CFLAGS@ @MUTTER_MESSAGE_CFLAGS@ \
-I$(top_srcdir)/src \
-DMUTTER_ICON_DIR=\"$(pkgdatadir)/icons\" \
-DMUTTER_LOCALEDIR=\"$(prefix)/@DATADIRNAME@/locale\"
mutter_message_SOURCES= \
mutter-message.c
mutter_window_demo_SOURCES= \
mutter-window-demo.c
mutter_mag_SOURCES= \
mutter-mag.c
mutter_grayscale_SOURCES= \
mutter-grayscale.c
bin_PROGRAMS=mutter-message mutter-window-demo
## cheesy hacks I use, don't really have any business existing. ;-)
noinst_PROGRAMS=mutter-mag mutter-grayscale
mutter_message_LDADD= @MUTTER_MESSAGE_LIBS@
mutter_window_demo_LDADD= @MUTTER_WINDOW_DEMO_LIBS@
mutter_mag_LDADD= @MUTTER_WINDOW_DEMO_LIBS@
mutter_grayscale_LDADD = @MUTTER_WINDOW_DEMO_LIBS@
EXTRA_DIST=$(icon_DATA)

View File

@ -1,107 +0,0 @@
/* Hack for grayscaling an image */
/*
* Copyright (C) 2002 Red Hat Inc.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License as
* published by the Free Software Foundation; either version 2 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
* 02111-1307, USA.
*/
#include <gdk-pixbuf/gdk-pixbuf.h>
#include <unistd.h>
#include <stdlib.h>
#include <math.h>
#define INTENSITY(r, g, b) ((r) * 0.30 + (g) * 0.59 + (b) * 0.11)
static GdkPixbuf*
grayscale_pixbuf (GdkPixbuf *pixbuf)
{
GdkPixbuf *gray;
guchar *pixels;
int rowstride;
int pixstride;
int row;
int n_rows;
int width;
gray = gdk_pixbuf_copy (pixbuf);
rowstride = gdk_pixbuf_get_rowstride (gray);
pixstride = gdk_pixbuf_get_has_alpha (gray) ? 4 : 3;
pixels = gdk_pixbuf_get_pixels (gray);
n_rows = gdk_pixbuf_get_height (gray);
width = gdk_pixbuf_get_width (gray);
row = 0;
while (row < n_rows)
{
guchar *p = pixels + row * rowstride;
guchar *end = p + (pixstride * width);
while (p != end)
{
double v = INTENSITY (p[0], p[1], p[2]);
p[0] = (guchar) v;
p[1] = (guchar) v;
p[2] = (guchar) v;
p += pixstride;
}
++row;
}
return gray;
}
int
main (int argc, char **argv)
{
GdkPixbuf *pixbuf;
GdkPixbuf *gray;
GError *err;
if (argc != 2)
{
g_printerr ("specify a single image on the command line\n");
return 1;
}
err = NULL;
pixbuf = gdk_pixbuf_new_from_file (argv[1], &err);
if (err != NULL)
{
g_printerr ("failed to load image: %s\n", err->message);
g_error_free (err);
return 1;
}
gray = grayscale_pixbuf (pixbuf);
err = NULL;
gdk_pixbuf_save (gray, "grayscale.png", "png", &err, NULL);
if (err != NULL)
{
g_printerr ("failed to save image: %s\n", err->message);
g_error_free (err);
return 1;
}
g_print ("wrote grayscale.png\n");
return 0;
}

View File

@ -1,299 +0,0 @@
/* Hack for use instead of xmag */
/*
* Copyright (C) 2002 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.
*/
#define _GNU_SOURCE
#define _XOPEN_SOURCE 600 /* C99 -- for rint() */
#include <gtk/gtk.h>
#include <gdk/gdkx.h>
#include <gdk/gdkkeysyms.h>
#include <unistd.h>
#include <stdlib.h>
#include <math.h>
static GtkWidget *grab_widget = NULL;
static GtkWidget *display_window = NULL;
static int last_grab_x = 0;
static int last_grab_y = 0;
static int last_grab_width = 150;
static int last_grab_height = 150;
static GtkAllocation last_grab_allocation;
static double width_factor = 4.0;
static double height_factor = 4.0;
static GdkInterpType interp_mode = GDK_INTERP_NEAREST;
static guint regrab_idle_id = 0;
static GdkPixbuf*
get_pixbuf (void)
{
GdkPixbuf *screenshot;
GdkPixbuf *magnified;
#if 0
g_print ("Size %d x %d\n",
last_grab_width, last_grab_height);
#endif
screenshot = gdk_pixbuf_get_from_window (gdk_get_default_root_window (),
last_grab_x, last_grab_y,
last_grab_width, last_grab_height);
if (screenshot == NULL)
{
g_printerr ("Screenshot failed\n");
exit (1);
}
magnified = gdk_pixbuf_scale_simple (screenshot, last_grab_width * width_factor,
last_grab_height * height_factor,
interp_mode);
g_object_unref (G_OBJECT (screenshot));
return magnified;
}
static gboolean
regrab_idle (GtkWidget *image)
{
GtkAllocation allocation;
GdkPixbuf *magnified;
gtk_widget_get_allocation (image, &allocation);
if (allocation.width != last_grab_allocation.width ||
allocation.height != last_grab_allocation.height)
{
last_grab_width = rint (allocation.width / width_factor);
last_grab_height = rint (allocation.height / height_factor);
last_grab_allocation = allocation;
magnified = get_pixbuf ();
gtk_image_set_from_pixbuf (GTK_IMAGE (image), magnified);
g_object_unref (G_OBJECT (magnified));
}
regrab_idle_id = 0;
return FALSE;
}
static void
image_resized (GtkWidget *image)
{
if (regrab_idle_id == 0)
regrab_idle_id = g_idle_add_full (G_PRIORITY_LOW + 100, (GSourceFunc) regrab_idle,
image, NULL);
}
static void
grab_area_at_mouse (GtkWidget *invisible,
int x_root,
int y_root)
{
GdkPixbuf *magnified;
int width, height;
GtkWidget *widget;
width = last_grab_width;
height = last_grab_height;
last_grab_x = x_root;
last_grab_y = y_root;
last_grab_width = width;
last_grab_height = height;
magnified = get_pixbuf ();
display_window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
gtk_window_set_default_size (GTK_WINDOW (display_window),
last_grab_width, last_grab_height);
widget = gtk_image_new_from_pixbuf (magnified);
gtk_widget_set_size_request (widget, 40, 40);
gtk_container_add (GTK_CONTAINER (display_window), widget);
g_object_unref (G_OBJECT (magnified));
g_object_add_weak_pointer (G_OBJECT (display_window),
(gpointer) &display_window);
g_signal_connect (G_OBJECT (display_window), "destroy",
G_CALLBACK (gtk_main_quit), NULL);
g_signal_connect_after (G_OBJECT (widget), "size_allocate", G_CALLBACK (image_resized), NULL);
gtk_widget_show_all (display_window);
}
static void
shutdown_grab (void)
{
GdkDeviceManager *manager;
GdkDevice *device;
manager = gdk_display_get_device_manager (gdk_display_get_default ());
device = gdk_device_manager_get_client_pointer (manager);
gdk_device_ungrab (device, gtk_get_current_event_time ());
gdk_device_ungrab (gdk_device_get_associated_device (device),
gtk_get_current_event_time ());
gtk_grab_remove (grab_widget);
}
static void
mouse_motion (GtkWidget *invisible,
GdkEventMotion *event,
gpointer data)
{
}
static gboolean
mouse_release (GtkWidget *invisible,
GdkEventButton *event,
gpointer data)
{
if (event->button != 1)
return FALSE;
grab_area_at_mouse (invisible, event->x_root, event->y_root);
shutdown_grab ();
g_signal_handlers_disconnect_by_func (invisible, mouse_motion, NULL);
g_signal_handlers_disconnect_by_func (invisible, mouse_release, NULL);
return TRUE;
}
/* Helper Functions */
static gboolean mouse_press (GtkWidget *invisible,
GdkEventButton *event,
gpointer data);
static gboolean
key_press (GtkWidget *invisible,
GdkEventKey *event,
gpointer data)
{
if (event->keyval == GDK_KEY_Escape)
{
shutdown_grab ();
g_signal_handlers_disconnect_by_func (invisible, mouse_press, NULL);
g_signal_handlers_disconnect_by_func (invisible, key_press, NULL);
gtk_main_quit ();
return TRUE;
}
return FALSE;
}
static gboolean
mouse_press (GtkWidget *invisible,
GdkEventButton *event,
gpointer data)
{
if (event->type == GDK_BUTTON_PRESS &&
event->button == 1)
{
g_signal_connect (invisible, "motion_notify_event",
G_CALLBACK (mouse_motion), NULL);
g_signal_connect (invisible, "button_release_event",
G_CALLBACK (mouse_release), NULL);
g_signal_handlers_disconnect_by_func (invisible, mouse_press, NULL);
g_signal_handlers_disconnect_by_func (invisible, key_press, NULL);
return TRUE;
}
return FALSE;
}
static void
begin_area_grab (void)
{
GdkWindow *window;
GdkDeviceManager *manager;
GdkDevice *device;
if (grab_widget == NULL)
{
grab_widget = gtk_invisible_new ();
gtk_widget_add_events (grab_widget,
GDK_BUTTON_RELEASE_MASK | GDK_BUTTON_PRESS_MASK | GDK_POINTER_MOTION_MASK);
gtk_widget_show (grab_widget);
}
window = gtk_widget_get_window (grab_widget);
manager = gdk_display_get_device_manager (gdk_display_get_default ());
device = gdk_device_manager_get_client_pointer (manager);
if (gdk_device_grab (device,
window,
GDK_OWNERSHIP_NONE,
FALSE,
GDK_BUTTON_RELEASE_MASK | GDK_BUTTON_PRESS_MASK | GDK_POINTER_MOTION_MASK,
NULL,
gtk_get_current_event_time ()) != GDK_GRAB_SUCCESS)
{
g_warning ("Failed to grab pointer to do eyedropper");
return;
}
if (gdk_device_grab (gdk_device_get_associated_device (device),
window,
GDK_OWNERSHIP_NONE,
FALSE,
GDK_KEY_PRESS_MASK | GDK_KEY_RELEASE_MASK,
NULL,
gtk_get_current_event_time ()) != GDK_GRAB_SUCCESS)
{
gdk_device_ungrab (device, gtk_get_current_event_time ());
g_warning ("Failed to grab keyboard to do eyedropper");
return;
}
gtk_grab_add (grab_widget);
g_signal_connect (grab_widget, "button_press_event",
G_CALLBACK (mouse_press), NULL);
g_signal_connect (grab_widget, "key_press_event",
G_CALLBACK (key_press), NULL);
}
int
main (int argc, char **argv)
{
gtk_init (&argc, &argv);
begin_area_grab ();
gtk_main ();
return 0;
}

View File

@ -1,160 +0,0 @@
/* Mutter send-magic-messages app */
/*
* Copyright (C) 2002 Havoc Pennington
*
* 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 <gtk/gtk.h>
#include <gdk/gdkx.h>
#include <stdlib.h>
#include <string.h>
#include <libintl.h>
#define _(x) dgettext (GETTEXT_PACKAGE, x)
#define N_(x) x
static Display *display;
static void
send_reload_theme (void)
{
XEvent xev;
xev.xclient.type = ClientMessage;
xev.xclient.serial = 0;
xev.xclient.send_event = True;
xev.xclient.display = display;
xev.xclient.window = gdk_x11_get_default_root_xwindow ();
xev.xclient.message_type = XInternAtom (display,
"_MUTTER_RELOAD_THEME_MESSAGE",
False);
xev.xclient.format = 32;
xev.xclient.data.l[0] = 0;
xev.xclient.data.l[1] = 0;
xev.xclient.data.l[2] = 0;
XSendEvent (display,
gdk_x11_get_default_root_xwindow (),
False,
SubstructureRedirectMask | SubstructureNotifyMask,
&xev);
XFlush (display);
XSync (display, False);
}
static void
send_set_keybindings (gboolean enabled)
{
XEvent xev;
xev.xclient.type = ClientMessage;
xev.xclient.serial = 0;
xev.xclient.send_event = True;
xev.xclient.display = display;
xev.xclient.window = gdk_x11_get_default_root_xwindow ();
xev.xclient.message_type = XInternAtom (display,
"_MUTTER_SET_KEYBINDINGS_MESSAGE",
False);
xev.xclient.format = 32;
xev.xclient.data.l[0] = enabled;
xev.xclient.data.l[1] = 0;
xev.xclient.data.l[2] = 0;
XSendEvent (display,
gdk_x11_get_default_root_xwindow (),
False,
SubstructureRedirectMask | SubstructureNotifyMask,
&xev);
XFlush (display);
XSync (display, False);
}
#ifdef WITH_VERBOSE_MODE
static void
send_toggle_verbose (void)
{
XEvent xev;
xev.xclient.type = ClientMessage;
xev.xclient.serial = 0;
xev.xclient.send_event = True;
xev.xclient.display = display;
xev.xclient.window = gdk_x11_get_default_root_xwindow ();
xev.xclient.message_type = XInternAtom (display,
"_MUTTER_TOGGLE_VERBOSE",
False);
xev.xclient.format = 32;
xev.xclient.data.l[0] = 0;
xev.xclient.data.l[1] = 0;
xev.xclient.data.l[2] = 0;
XSendEvent (display,
gdk_x11_get_default_root_xwindow (),
False,
SubstructureRedirectMask | SubstructureNotifyMask,
&xev);
XFlush (display);
XSync (display, False);
}
#endif
static void
usage (void)
{
g_printerr (_("Usage: %s\n"),
"mutter-message (reload-theme|enable-keybindings|disable-keybindings|toggle-verbose)");
exit (1);
}
int
main (int argc, char **argv)
{
bind_textdomain_codeset (GETTEXT_PACKAGE, "UTF-8");
gtk_init (&argc, &argv);
if (argc < 2)
usage ();
display = GDK_DISPLAY_XDISPLAY (gdk_display_get_default ());
if (strcmp (argv[1], "reload-theme") == 0)
send_reload_theme ();
else if (strcmp (argv[1], "enable-keybindings") == 0)
send_set_keybindings (TRUE);
else if (strcmp (argv[1], "disable-keybindings") == 0)
send_set_keybindings (FALSE);
else if (strcmp (argv[1], "toggle-verbose") == 0)
{
#ifndef WITH_VERBOSE_MODE
g_printerr (_("Mutter was compiled without support for verbose mode\n"));
return 1;
#else
send_toggle_verbose ();
#endif
}
else
usage ();
return 0;
}

File diff suppressed because it is too large Load Diff

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.4 KiB

View File

@ -739,22 +739,6 @@ meta_ui_frame_get_corner_radiuses (MetaFrames *frames,
*bottom_right = fgeom.bottom_right_corner_rounded_radius + sqrt(fgeom.bottom_right_corner_rounded_radius);
}
void
meta_frames_get_corner_radiuses (MetaFrames *frames,
Window xwindow,
float *top_left,
float *top_right,
float *bottom_left,
float *bottom_right)
{
MetaUIFrame *frame;
frame = meta_frames_lookup_window (frames, xwindow);
meta_ui_frame_get_corner_radiuses (frames, frame, top_left, top_right,
bottom_left, bottom_right);
}
void
meta_frames_reset_bg (MetaFrames *frames,
Window xwindow)
@ -1851,6 +1835,102 @@ clip_region_to_visible_frame_border (cairo_region_t *region,
cairo_region_destroy (frame_border);
}
#define TAU (2*M_PI)
/*
* Draw the opaque and semi-opaque pixels of this frame into a mask.
*
* (0,0) in Cairo coordinates is assumed to be the top left corner of the
* invisible border.
*
* The parts of @cr's surface in the clip region are assumed to be
* initialized to fully-transparent, and the clip region is assumed to
* contain the invisible border and the visible parts of the frame, but
* not the client area.
*
* This function uses @cr to draw pixels of arbitrary color (it will
* typically be drawing in a %CAIRO_FORMAT_A8 surface, so the color is
* discarded anyway) with appropriate alpha values to reproduce this
* frame's alpha channel, as a mask to be applied to an opaque pixmap.
*
* @frame: This frame
* @xwindow: The X window for the frame, which has the client window as a child
* @width: The width of the framed window including any invisible borders
* @height: The height of the framed window including any invisible borders
* @cr: Used to draw the resulting mask
*/
void
meta_frames_get_mask (MetaFrames *frames,
Window xwindow,
guint width,
guint height,
cairo_t *cr)
{
MetaUIFrame *frame = meta_frames_lookup_window (frames, xwindow);
float top_left, top_right, bottom_left, bottom_right;
int x, y;
MetaFrameBorders borders;
if (frame == NULL)
meta_bug ("No such frame 0x%lx\n", xwindow);
cairo_save (cr);
meta_ui_frame_get_borders (frames, frame, &borders);
meta_ui_frame_get_corner_radiuses (frames, frame,
&top_left, &top_right,
&bottom_left, &bottom_right);
/* top left */
x = borders.invisible.left;
y = borders.invisible.top;
cairo_arc (cr,
x + top_left,
y + top_left,
top_left,
2 * TAU / 4,
3 * TAU / 4);
/* top right */
x = width - borders.invisible.right - top_right;
y = borders.invisible.top;
cairo_arc (cr,
x,
y + top_right,
top_right,
3 * TAU / 4,
4 * TAU / 4);
/* bottom right */
x = width - borders.invisible.right - bottom_right;
y = height - borders.invisible.bottom - bottom_right;
cairo_arc (cr,
x,
y,
bottom_right,
0 * TAU / 4,
1 * TAU / 4);
/* bottom left */
x = borders.invisible.left;
y = height - borders.invisible.bottom - bottom_left;
cairo_arc (cr,
x + bottom_left,
y,
bottom_left,
1 * TAU / 4,
2 * TAU / 4);
cairo_set_source_rgba (cr, 1, 1, 1, 1);
cairo_fill (cr);
cairo_restore (cr);
}
static gboolean
meta_frames_draw (GtkWidget *widget,
cairo_t *cr)

View File

@ -140,12 +140,11 @@ cairo_region_t *meta_frames_get_frame_bounds (MetaFrames *frames,
int window_width,
int window_height);
void meta_frames_get_corner_radiuses (MetaFrames *frames,
Window xwindow,
float *top_left,
float *top_right,
float *bottom_left,
float *bottom_right);
void meta_frames_get_mask (MetaFrames *frames,
Window xwindow,
guint width,
guint height,
cairo_t *cr);
void meta_frames_move_resize_frame (MetaFrames *frames,
Window xwindow,

View File

@ -40,7 +40,6 @@ typedef enum
{
MENU_ITEM_SEPARATOR = 0,
MENU_ITEM_NORMAL,
MENU_ITEM_IMAGE,
MENU_ITEM_CHECKBOX,
MENU_ITEM_RADIOBUTTON,
MENU_ITEM_WORKSPACE_LIST,
@ -50,7 +49,6 @@ struct _MenuItem
{
MetaMenuOp op;
MetaMenuItemType type;
const char *stock_id;
const gboolean checked;
const char *label;
};
@ -66,42 +64,42 @@ static void activate_cb (GtkWidget *menuitem, gpointer data);
static MenuItem menuitems[] = {
/* Translators: Translate this string the same way as you do in libwnck! */
{ META_MENU_OP_MINIMIZE, MENU_ITEM_IMAGE, METACITY_STOCK_MINIMIZE, FALSE, N_("Mi_nimize") },
{ META_MENU_OP_MINIMIZE, MENU_ITEM_NORMAL, FALSE, N_("Mi_nimize") },
/* Translators: Translate this string the same way as you do in libwnck! */
{ META_MENU_OP_MAXIMIZE, MENU_ITEM_IMAGE, METACITY_STOCK_MAXIMIZE, FALSE, N_("Ma_ximize") },
{ META_MENU_OP_MAXIMIZE, MENU_ITEM_NORMAL, FALSE, N_("Ma_ximize") },
/* Translators: Translate this string the same way as you do in libwnck! */
{ META_MENU_OP_UNMAXIMIZE, MENU_ITEM_NORMAL, NULL, FALSE, N_("Unma_ximize") },
{ META_MENU_OP_UNMAXIMIZE, MENU_ITEM_NORMAL, FALSE, N_("Unma_ximize") },
/* Translators: Translate this string the same way as you do in libwnck! */
{ META_MENU_OP_SHADE, MENU_ITEM_NORMAL, NULL, FALSE, N_("Roll _Up") },
{ META_MENU_OP_SHADE, MENU_ITEM_NORMAL, FALSE, N_("Roll _Up") },
/* Translators: Translate this string the same way as you do in libwnck! */
{ META_MENU_OP_UNSHADE, MENU_ITEM_NORMAL, NULL, FALSE, N_("_Unroll") },
{ META_MENU_OP_UNSHADE, MENU_ITEM_NORMAL, FALSE, N_("_Unroll") },
/* Translators: Translate this string the same way as you do in libwnck! */
{ META_MENU_OP_MOVE, MENU_ITEM_NORMAL, NULL, FALSE, N_("_Move") },
{ META_MENU_OP_MOVE, MENU_ITEM_NORMAL, FALSE, N_("_Move") },
/* Translators: Translate this string the same way as you do in libwnck! */
{ META_MENU_OP_RESIZE, MENU_ITEM_NORMAL, NULL, FALSE, N_("_Resize") },
{ META_MENU_OP_RESIZE, MENU_ITEM_NORMAL, FALSE, N_("_Resize") },
/* Translators: Translate this string the same way as you do in libwnck! */
{ META_MENU_OP_RECOVER, MENU_ITEM_NORMAL, NULL, FALSE, N_("Move Titlebar On_screen") },
{ META_MENU_OP_WORKSPACES, MENU_ITEM_SEPARATOR, NULL, FALSE, NULL }, /* separator */
{ META_MENU_OP_RECOVER, MENU_ITEM_NORMAL, FALSE, N_("Move Titlebar On_screen") },
{ META_MENU_OP_WORKSPACES, MENU_ITEM_SEPARATOR, FALSE, NULL }, /* separator */
/* Translators: Translate this string the same way as you do in libwnck! */
{ META_MENU_OP_ABOVE, MENU_ITEM_CHECKBOX, NULL, FALSE, N_("Always on _Top") },
{ META_MENU_OP_ABOVE, MENU_ITEM_CHECKBOX, FALSE, N_("Always on _Top") },
/* Translators: Translate this string the same way as you do in libwnck! */
{ META_MENU_OP_UNABOVE, MENU_ITEM_CHECKBOX, NULL, TRUE, N_("Always on _Top") },
{ META_MENU_OP_UNABOVE, MENU_ITEM_CHECKBOX, TRUE, N_("Always on _Top") },
/* Translators: Translate this string the same way as you do in libwnck! */
{ META_MENU_OP_STICK, MENU_ITEM_RADIOBUTTON, NULL, FALSE, N_("_Always on Visible Workspace") },
{ META_MENU_OP_STICK, MENU_ITEM_RADIOBUTTON, FALSE, N_("_Always on Visible Workspace") },
/* Translators: Translate this string the same way as you do in libwnck! */
{ META_MENU_OP_UNSTICK, MENU_ITEM_RADIOBUTTON, NULL, FALSE, N_("_Only on This Workspace") },
{ META_MENU_OP_UNSTICK, MENU_ITEM_RADIOBUTTON, FALSE, N_("_Only on This Workspace") },
/* Translators: Translate this string the same way as you do in libwnck! */
{ META_MENU_OP_MOVE_LEFT, MENU_ITEM_NORMAL, NULL, FALSE, N_("Move to Workspace _Left") },
{ META_MENU_OP_MOVE_LEFT, MENU_ITEM_NORMAL, FALSE, N_("Move to Workspace _Left") },
/* Translators: Translate this string the same way as you do in libwnck! */
{ META_MENU_OP_MOVE_RIGHT, MENU_ITEM_NORMAL, NULL, FALSE, N_("Move to Workspace R_ight") },
{ META_MENU_OP_MOVE_RIGHT, MENU_ITEM_NORMAL, FALSE, N_("Move to Workspace R_ight") },
/* Translators: Translate this string the same way as you do in libwnck! */
{ META_MENU_OP_MOVE_UP, MENU_ITEM_NORMAL, NULL, FALSE, N_("Move to Workspace _Up") },
{ META_MENU_OP_MOVE_UP, MENU_ITEM_NORMAL, FALSE, N_("Move to Workspace _Up") },
/* Translators: Translate this string the same way as you do in libwnck! */
{ META_MENU_OP_MOVE_DOWN, MENU_ITEM_NORMAL, NULL, FALSE, N_("Move to Workspace _Down") },
{ 0, MENU_ITEM_WORKSPACE_LIST, NULL, FALSE, NULL },
{ 0, MENU_ITEM_SEPARATOR, NULL, FALSE, NULL }, /* separator */
{ META_MENU_OP_MOVE_DOWN, MENU_ITEM_NORMAL, FALSE, N_("Move to Workspace _Down") },
{ 0, MENU_ITEM_WORKSPACE_LIST, FALSE, NULL },
{ 0, MENU_ITEM_SEPARATOR, FALSE, NULL }, /* separator */
/* Translators: Translate this string the same way as you do in libwnck! */
{ META_MENU_OP_DELETE, MENU_ITEM_IMAGE, METACITY_STOCK_DELETE, FALSE, N_("_Close") }
{ META_MENU_OP_DELETE, MENU_ITEM_NORMAL, FALSE, N_("_Close") }
};
static void
@ -274,16 +272,6 @@ menu_item_new (MenuItem *menuitem, int workspace_id)
{
mi = gtk_menu_item_new ();
}
else if (menuitem->type == MENU_ITEM_IMAGE)
{
GtkWidget *image;
image = gtk_image_new_from_stock (menuitem->stock_id, GTK_ICON_SIZE_MENU);
mi = gtk_image_menu_item_new ();
gtk_image_menu_item_set_image (GTK_IMAGE_MENU_ITEM (mi), image);
gtk_widget_show (image);
}
else if (menuitem->type == MENU_ITEM_CHECKBOX)
{
mi = gtk_check_menu_item_new ();
@ -392,8 +380,7 @@ meta_window_menu_new (MetaFrames *frames,
int j;
MenuItem to_another_workspace = {
0, MENU_ITEM_NORMAL,
NULL, FALSE,
0, MENU_ITEM_NORMAL, FALSE,
N_("Move to Another _Workspace")
};

View File

@ -27,11 +27,6 @@
#include <gtk/gtk.h>
#include "frames.h"
/* Stock icons */
#define METACITY_STOCK_DELETE "metacity-delete"
#define METACITY_STOCK_MINIMIZE "metacity-minimize"
#define METACITY_STOCK_MAXIMIZE "metacity-maximize"
struct _MetaWindowMenu
{
MetaFrames *frames;

View File

@ -1,496 +0,0 @@
/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
/*
* Copyright (C) 2002 Havoc Pennington
*
* 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.
*/
/*
* SECTION:preview-widget
* @title: MetaPreview
* @short_description: Mutter theme preview widget
*/
#define _GNU_SOURCE
#define _XOPEN_SOURCE 600 /* for the maths routines over floats */
#include <math.h>
#include <gtk/gtk.h>
#include <meta/preview-widget.h>
#include "theme-private.h"
static void meta_preview_get_preferred_width (GtkWidget *widget,
gint *minimum,
gint *natural);
static void meta_preview_get_preferred_height (GtkWidget *widget,
gint *minimum,
gint *natural);
static void meta_preview_size_allocate (GtkWidget *widget,
GtkAllocation *allocation);
static gboolean meta_preview_draw (GtkWidget *widget,
cairo_t *cr);
static void meta_preview_realize (GtkWidget *widget);
static void meta_preview_dispose (GObject *object);
static void meta_preview_finalize (GObject *object);
G_DEFINE_TYPE (MetaPreview, meta_preview, GTK_TYPE_BIN);
static void
meta_preview_class_init (MetaPreviewClass *class)
{
GObjectClass *gobject_class = G_OBJECT_CLASS (class);
GtkWidgetClass *widget_class;
widget_class = (GtkWidgetClass*) class;
gobject_class->dispose = meta_preview_dispose;
gobject_class->finalize = meta_preview_finalize;
widget_class->realize = meta_preview_realize;
widget_class->draw = meta_preview_draw;
widget_class->get_preferred_width = meta_preview_get_preferred_width;
widget_class->get_preferred_height = meta_preview_get_preferred_height;
widget_class->size_allocate = meta_preview_size_allocate;
gtk_container_class_handle_border_width (GTK_CONTAINER_CLASS (class));
}
static void
meta_preview_init (MetaPreview *preview)
{
int i;
gtk_widget_set_has_window (GTK_WIDGET (preview), FALSE);
i = 0;
while (i < MAX_BUTTONS_PER_CORNER)
{
preview->button_layout.left_buttons[i] = META_BUTTON_FUNCTION_LAST;
preview->button_layout.right_buttons[i] = META_BUTTON_FUNCTION_LAST;
++i;
}
preview->button_layout.left_buttons[0] = META_BUTTON_FUNCTION_MENU;
preview->button_layout.right_buttons[0] = META_BUTTON_FUNCTION_MINIMIZE;
preview->button_layout.right_buttons[1] = META_BUTTON_FUNCTION_MAXIMIZE;
preview->button_layout.right_buttons[2] = META_BUTTON_FUNCTION_CLOSE;
preview->type = META_FRAME_TYPE_NORMAL;
preview->flags =
META_FRAME_ALLOWS_DELETE |
META_FRAME_ALLOWS_MENU |
META_FRAME_ALLOWS_MINIMIZE |
META_FRAME_ALLOWS_MAXIMIZE |
META_FRAME_ALLOWS_VERTICAL_RESIZE |
META_FRAME_ALLOWS_HORIZONTAL_RESIZE |
META_FRAME_HAS_FOCUS |
META_FRAME_ALLOWS_SHADE |
META_FRAME_ALLOWS_MOVE;
preview->borders_cached = FALSE;
}
GtkWidget*
meta_preview_new (void)
{
MetaPreview *preview;
preview = g_object_new (META_TYPE_PREVIEW, NULL);
return GTK_WIDGET (preview);
}
static void
meta_preview_dispose (GObject *object)
{
MetaPreview *preview = META_PREVIEW (object);
g_clear_object (&preview->style_context);
G_OBJECT_CLASS (meta_preview_parent_class)->dispose (object);
}
static void
meta_preview_finalize (GObject *object)
{
MetaPreview *preview;
preview = META_PREVIEW (object);
g_free (preview->title);
preview->title = NULL;
G_OBJECT_CLASS (meta_preview_parent_class)->finalize (object);
}
static void
ensure_info (MetaPreview *preview)
{
GtkWidget *widget;
widget = GTK_WIDGET (preview);
if (preview->layout == NULL)
{
PangoFontDescription *font_desc;
double scale;
PangoAttrList *attrs;
PangoAttribute *attr;
if (preview->theme)
scale = meta_theme_get_title_scale (preview->theme,
preview->type,
preview->flags);
else
scale = 1.0;
preview->layout = gtk_widget_create_pango_layout (widget,
preview->title);
font_desc = meta_gtk_widget_get_font_desc (widget, scale, NULL);
preview->text_height =
meta_pango_font_desc_get_text_height (font_desc,
gtk_widget_get_pango_context (widget));
attrs = pango_attr_list_new ();
attr = pango_attr_size_new (pango_font_description_get_size (font_desc));
attr->start_index = 0;
attr->end_index = G_MAXINT;
pango_attr_list_insert (attrs, attr);
pango_layout_set_attributes (preview->layout, attrs);
pango_attr_list_unref (attrs);
pango_font_description_free (font_desc);
}
if (!preview->borders_cached)
{
if (preview->theme)
meta_theme_get_frame_borders (preview->theme,
preview->type,
preview->text_height,
preview->flags,
&preview->borders);
else
meta_frame_borders_clear (&preview->borders);
preview->borders_cached = TRUE;
}
}
static gboolean
meta_preview_draw (GtkWidget *widget,
cairo_t *cr)
{
MetaPreview *preview = META_PREVIEW (widget);
GtkAllocation allocation;
gtk_widget_get_allocation (widget, &allocation);
if (preview->theme)
{
int client_width;
int client_height;
MetaButtonState button_states[META_BUTTON_TYPE_LAST] =
{
META_BUTTON_STATE_NORMAL,
META_BUTTON_STATE_NORMAL,
META_BUTTON_STATE_NORMAL,
META_BUTTON_STATE_NORMAL
};
ensure_info (preview);
cairo_save (cr);
client_width = allocation.width - preview->borders.total.left - preview->borders.total.right;
client_height = allocation.height - preview->borders.total.top - preview->borders.total.bottom;
if (client_width < 0)
client_width = 1;
if (client_height < 0)
client_height = 1;
meta_theme_draw_frame (preview->theme,
preview->style_context,
cr,
preview->type,
preview->flags,
client_width, client_height,
preview->layout,
preview->text_height,
&preview->button_layout,
button_states,
meta_preview_get_mini_icon (),
meta_preview_get_icon ());
cairo_restore (cr);
}
/* draw child */
return GTK_WIDGET_CLASS (meta_preview_parent_class)->draw (widget, cr);
}
static void
meta_preview_realize (GtkWidget *widget)
{
MetaPreview *preview = META_PREVIEW (widget);
GTK_WIDGET_CLASS (meta_preview_parent_class)->realize (widget);
preview->style_context = meta_theme_create_style_context (gtk_widget_get_screen (widget),
NULL);
}
#define NO_CHILD_WIDTH 80
#define NO_CHILD_HEIGHT 20
static void
meta_preview_get_preferred_width (GtkWidget *widget,
gint *minimum,
gint *natural)
{
MetaPreview *preview;
GtkWidget *child;
preview = META_PREVIEW (widget);
ensure_info (preview);
*minimum = *natural = preview->borders.total.left + preview->borders.total.right;
child = gtk_bin_get_child (GTK_BIN (preview));
if (child && gtk_widget_get_visible (child))
{
gint child_min, child_nat;
gtk_widget_get_preferred_width (child, &child_min, &child_nat);
*minimum += child_min;
*natural += child_nat;
}
else
{
*minimum += NO_CHILD_WIDTH;
*natural += NO_CHILD_WIDTH;
}
}
static void
meta_preview_get_preferred_height (GtkWidget *widget,
gint *minimum,
gint *natural)
{
MetaPreview *preview;
GtkWidget *child;
preview = META_PREVIEW (widget);
ensure_info (preview);
*minimum = *natural = preview->borders.total.top + preview->borders.total.bottom;
child = gtk_bin_get_child (GTK_BIN (preview));
if (child && gtk_widget_get_visible (child))
{
gint child_min, child_nat;
gtk_widget_get_preferred_height (child, &child_min, &child_nat);
*minimum += child_min;
*natural += child_nat;
}
else
{
*minimum += NO_CHILD_HEIGHT;
*natural += NO_CHILD_HEIGHT;
}
}
static void
meta_preview_size_allocate (GtkWidget *widget,
GtkAllocation *allocation)
{
MetaPreview *preview;
GtkAllocation widget_allocation, child_allocation;
GtkWidget *child;
preview = META_PREVIEW (widget);
ensure_info (preview);
gtk_widget_set_allocation (widget, allocation);
child = gtk_bin_get_child (GTK_BIN (widget));
if (child && gtk_widget_get_visible (child))
{
gtk_widget_get_allocation (widget, &widget_allocation);
child_allocation.x = widget_allocation.x + preview->borders.total.left;
child_allocation.y = widget_allocation.y + preview->borders.total.top;
child_allocation.width = MAX (1, widget_allocation.width - preview->borders.total.left - preview->borders.total.right);
child_allocation.height = MAX (1, widget_allocation.height - preview->borders.total.top - preview->borders.total.bottom);
gtk_widget_size_allocate (child, &child_allocation);
}
}
static void
clear_cache (MetaPreview *preview)
{
if (preview->layout)
{
g_object_unref (G_OBJECT (preview->layout));
preview->layout = NULL;
}
preview->borders_cached = FALSE;
}
void
meta_preview_set_theme (MetaPreview *preview,
MetaTheme *theme)
{
g_return_if_fail (META_IS_PREVIEW (preview));
preview->theme = theme;
clear_cache (preview);
gtk_widget_queue_resize (GTK_WIDGET (preview));
}
void
meta_preview_set_title (MetaPreview *preview,
const char *title)
{
g_return_if_fail (META_IS_PREVIEW (preview));
g_free (preview->title);
preview->title = g_strdup (title);
clear_cache (preview);
gtk_widget_queue_resize (GTK_WIDGET (preview));
}
void
meta_preview_set_frame_type (MetaPreview *preview,
MetaFrameType type)
{
g_return_if_fail (META_IS_PREVIEW (preview));
preview->type = type;
clear_cache (preview);
gtk_widget_queue_resize (GTK_WIDGET (preview));
}
void
meta_preview_set_frame_flags (MetaPreview *preview,
MetaFrameFlags flags)
{
g_return_if_fail (META_IS_PREVIEW (preview));
preview->flags = flags;
clear_cache (preview);
gtk_widget_queue_resize (GTK_WIDGET (preview));
}
void
meta_preview_set_button_layout (MetaPreview *preview,
const MetaButtonLayout *button_layout)
{
g_return_if_fail (META_IS_PREVIEW (preview));
preview->button_layout = *button_layout;
gtk_widget_queue_draw (GTK_WIDGET (preview));
}
GdkPixbuf*
meta_preview_get_icon (void)
{
static GdkPixbuf *default_icon = NULL;
if (default_icon == NULL)
{
GtkIconTheme *theme;
gboolean icon_exists;
theme = gtk_icon_theme_get_default ();
icon_exists = gtk_icon_theme_has_icon (theme, META_DEFAULT_ICON_NAME);
if (icon_exists)
default_icon = gtk_icon_theme_load_icon (theme,
META_DEFAULT_ICON_NAME,
META_ICON_WIDTH,
0,
NULL);
else
default_icon = gtk_icon_theme_load_icon (theme,
"gtk-missing-image",
META_ICON_WIDTH,
0,
NULL);
g_assert (default_icon);
}
return default_icon;
}
GdkPixbuf*
meta_preview_get_mini_icon (void)
{
static GdkPixbuf *default_icon = NULL;
if (default_icon == NULL)
{
GtkIconTheme *theme;
gboolean icon_exists;
theme = gtk_icon_theme_get_default ();
icon_exists = gtk_icon_theme_has_icon (theme, META_DEFAULT_ICON_NAME);
if (icon_exists)
default_icon = gtk_icon_theme_load_icon (theme,
META_DEFAULT_ICON_NAME,
META_MINI_ICON_WIDTH,
0,
NULL);
else
default_icon = gtk_icon_theme_load_icon (theme,
"gtk-missing-image",
META_MINI_ICON_WIDTH,
0,
NULL);
g_assert (default_icon);
}
return default_icon;
}

View File

@ -857,10 +857,7 @@ meta_convert_meta_to_wnck (MetaWindow *window, MetaScreen *screen)
WnckWindowDisplayInfo wnck_window;
wnck_window.icon = window->icon;
wnck_window.mini_icon = window->mini_icon;
wnck_window.is_active = FALSE;
if (window == window->display->expected_focus_window)
wnck_window.is_active = TRUE;
wnck_window.is_active = window->has_focus;
if (window->frame)
{

File diff suppressed because it is too large Load Diff

View File

@ -1880,7 +1880,7 @@ debug_print_tokens (PosToken *tokens,
/**
* pos_tokenize:
* @expr: The expression
* @tokens_p: (out) The resulting tokens
* @tokens_p: (out): The resulting tokens
* @n_tokens_p: (out): The number of resulting tokens
* @err: (out): set to the problem if there was a problem
@ -5063,16 +5063,14 @@ meta_theme_get_current (void)
}
void
meta_theme_set_current (const char *name,
gboolean force_reload)
meta_theme_set_current (const char *name)
{
MetaTheme *new_theme;
GError *err;
meta_topic (META_DEBUG_THEMES, "Setting current theme to \"%s\"\n", name);
if (!force_reload &&
meta_current_theme &&
if (meta_current_theme &&
strcmp (name, meta_current_theme->name) == 0)
return;

View File

@ -4,7 +4,6 @@
/*
* Copyright (C) 2002 Havoc Pennington
* stock icon code Copyright (C) 2002 Jorn Baayen <jorn@nl.linux.org>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License as
@ -31,13 +30,10 @@
#include "core.h"
#include "theme-private.h"
#include "inlinepixbufs.h"
#include <string.h>
#include <stdlib.h>
#include <cairo-xlib.h>
static void meta_stock_icons_init (void);
static void meta_ui_accelerator_parse (const char *accel,
guint *keysym,
guint *keycode,
@ -62,8 +58,6 @@ meta_ui_init (void)
{
if (!gtk_init_check (NULL, NULL))
meta_fatal ("Unable to open X display %s\n", XDisplayName (NULL));
meta_stock_icons_init ();
}
Display*
@ -324,6 +318,16 @@ meta_ui_free (MetaUI *ui)
g_free (ui);
}
void
meta_ui_get_frame_mask (MetaUI *ui,
Window frame_xwindow,
guint width,
guint height,
cairo_t *cr)
{
meta_frames_get_mask (ui->frames, frame_xwindow, width, height, cr);
}
void
meta_ui_get_frame_borders (MetaUI *ui,
Window frame_xwindow,
@ -333,19 +337,6 @@ meta_ui_get_frame_borders (MetaUI *ui,
borders);
}
void
meta_ui_get_corner_radiuses (MetaUI *ui,
Window xwindow,
float *top_left,
float *top_right,
float *bottom_left,
float *bottom_right)
{
meta_frames_get_corner_radiuses (ui->frames, xwindow,
top_left, top_right,
bottom_left, bottom_right);
}
Window
meta_ui_create_frame_window (MetaUI *ui,
Display *xdisplay,
@ -778,10 +769,9 @@ meta_ui_theme_get_frame_borders (MetaUI *ui,
}
void
meta_ui_set_current_theme (const char *name,
gboolean force_reload)
meta_ui_set_current_theme (const char *name)
{
meta_theme_set_current (name, force_reload);
meta_theme_set_current (name);
meta_invalidate_default_icons ();
}
@ -1001,48 +991,6 @@ meta_ui_window_is_widget (MetaUI *ui,
return FALSE;
}
/* stock icon code Copyright (C) 2002 Jorn Baayen <jorn@nl.linux.org> */
typedef struct
{
char *stock_id;
const guint8 *icon_data;
} MetaStockIcon;
static void
meta_stock_icons_init (void)
{
GtkIconFactory *factory;
int i;
MetaStockIcon items[] =
{
{ METACITY_STOCK_DELETE, stock_delete_data },
{ METACITY_STOCK_MINIMIZE, stock_minimize_data },
{ METACITY_STOCK_MAXIMIZE, stock_maximize_data }
};
factory = gtk_icon_factory_new ();
gtk_icon_factory_add_default (factory);
for (i = 0; i < (gint) G_N_ELEMENTS (items); i++)
{
GtkIconSet *icon_set;
GdkPixbuf *pixbuf;
pixbuf = gdk_pixbuf_new_from_inline (-1, items[i].icon_data,
FALSE,
NULL);
icon_set = gtk_icon_set_new_from_pixbuf (pixbuf);
gtk_icon_factory_add (factory, items[i].stock_id, icon_set);
gtk_icon_set_unref (icon_set);
g_object_unref (G_OBJECT (pixbuf));
}
g_object_unref (G_OBJECT (factory));
}
int
meta_ui_get_drag_threshold (MetaUI *ui)
{

View File

@ -66,6 +66,13 @@ void meta_ui_theme_get_frame_borders (MetaUI *ui,
void meta_ui_get_frame_borders (MetaUI *ui,
Window frame_xwindow,
MetaFrameBorders *borders);
void meta_ui_get_frame_mask (MetaUI *ui,
Window frame_xwindow,
guint width,
guint height,
cairo_t *cr);
Window meta_ui_create_frame_window (MetaUI *ui,
Display *xdisplay,
Visual *xvisual,
@ -102,13 +109,6 @@ cairo_region_t *meta_ui_get_frame_bounds (MetaUI *ui,
int window_width,
int window_height);
void meta_ui_get_corner_radiuses (MetaUI *ui,
Window xwindow,
float *top_left,
float *top_right,
float *bottom_left,
float *bottom_right);
void meta_ui_queue_frame_draw (MetaUI *ui,
Window xwindow);
@ -154,8 +154,7 @@ gboolean meta_ui_window_should_not_cause_focus (Display *xdisplay,
char* meta_text_property_to_utf8 (Display *xdisplay,
const XTextProperty *prop);
void meta_ui_set_current_theme (const char *name,
gboolean force_reload);
void meta_ui_set_current_theme (const char *name);
gboolean meta_ui_have_a_theme (void);
/* Not a real key symbol but means "key above the tab key"; this is

436
src/wayland/meta-tty.c Normal file
View File

@ -0,0 +1,436 @@
/*
* Copyright © 2010 Intel Corporation
* 2013 Red Hat, Inc.
*
* Permission to use, copy, modify, distribute, and sell this software and
* its documentation for any purpose is hereby granted without fee, provided
* that the above copyright notice appear in all copies and that both that
* copyright notice and this permission notice appear in supporting
* documentation, and that the name of the copyright holders not be used in
* advertising or publicity pertaining to distribution of the software
* without specific, written prior permission. The copyright holders make
* no representations about the suitability of this software for any
* purpose. It is provided "as is" without express or implied warranty.
*
* THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS
* SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
* FITNESS, IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY
* SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER
* RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF
* CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
* CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
#include "config.h"
#include <termios.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <fcntl.h>
#include <errno.h>
#include <signal.h>
#include <linux/kd.h>
#include <linux/vt.h>
#include <linux/major.h>
#include <sys/ioctl.h>
#include <sys/types.h>
#include <sys/stat.h>
#include "meta-tty.h"
#include <gio/gio.h>
#include <glib-unix.h>
/* Introduced in 2.6.38 */
#ifndef K_OFF
#define K_OFF 0x04
#endif
struct _MetaTTYClass
{
GObjectClass parent_class;
};
struct _MetaTTY
{
GObject parent;
int fd;
struct termios terminal_attributes;
GMainContext *nested_context;
GMainLoop *nested_loop;
int input_source;
GSource *vt_enter_source, *vt_leave_source;
GSource *nested_term;
int vt, starting_vt;
int kb_mode;
};
enum {
SIGNAL_ENTER,
SIGNAL_LEAVE,
SIGNAL_LAST
};
static int signals[SIGNAL_LAST];
static void meta_tty_initable_iface_init (GInitableIface *);
G_DEFINE_TYPE_WITH_CODE (MetaTTY, meta_tty, G_TYPE_OBJECT,
G_IMPLEMENT_INTERFACE (G_TYPE_INITABLE,
meta_tty_initable_iface_init));
static gboolean
quit_nested_loop (gpointer user_data)
{
MetaTTY *tty = user_data;
g_main_loop_quit (tty->nested_loop);
return FALSE;
}
static gboolean
vt_release_handler (gpointer user_data)
{
MetaTTY *tty = user_data;
g_signal_emit (tty, signals[SIGNAL_LEAVE], 0);
ioctl (tty->fd, VT_RELDISP, 1);
/* We can't do anything at this point, because we don't
have input devices and we don't have the DRM master,
so let's run a nested busy loop until the VT is reentered */
g_main_loop_run (tty->nested_loop);
ioctl (tty->fd, VT_RELDISP, VT_ACKACQ);
g_signal_emit (tty, signals[SIGNAL_ENTER], 0);
return FALSE;
}
static int
on_tty_input (int fd,
GIOCondition mask,
gpointer user_data)
{
MetaTTY *tty = user_data;
/* Ignore input to tty. We get keyboard events from evdev */
tcflush(tty->fd, TCIFLUSH);
return 1;
}
static int
try_open_vt (MetaTTY *tty,
GError **error)
{
int tty0, fd;
char filename[16];
tty0 = open ("/dev/tty0", O_WRONLY | O_CLOEXEC);
if (tty0 < 0)
{
g_set_error (error, G_IO_ERROR,
g_io_error_from_errno (errno),
"Could not open tty0: %s", strerror (errno));
return -1;
}
if (ioctl (tty0, VT_OPENQRY, &tty->vt) < 0 || tty->vt == -1) {
g_set_error (error, G_IO_ERROR,
g_io_error_from_errno (errno),
"Could not open tty0: %s", strerror (errno));
close (tty0);
return -1;
}
close (tty0);
snprintf (filename, sizeof filename, "/dev/tty%d", tty->vt);
g_debug("compositor: using new vt %s\n", filename);
fd = open (filename, O_RDWR | O_NOCTTY | O_CLOEXEC);
return fd;
}
gboolean
meta_tty_activate_vt (MetaTTY *tty,
int vt,
GError **error)
{
if (ioctl(tty->fd, VT_ACTIVATE, vt) < 0)
{
g_set_error (error, G_IO_ERROR, g_io_error_from_errno (errno),
strerror (errno));
return FALSE;
}
else
return TRUE;
}
static int
env_get_fd (const char *env)
{
const char *value;
value = g_getenv (env);
if (value == NULL)
return -1;
else
return g_ascii_strtoll (value, NULL, 10);
}
static gboolean
meta_tty_initable_init(GInitable *initable,
GCancellable *cancellable,
GError **error)
{
MetaTTY *tty = META_TTY (initable);
struct termios raw_attributes;
struct vt_mode mode = { 0 };
int ret;
struct stat buf;
struct vt_stat vts;
tty->fd = env_get_fd ("WESTON_TTY_FD");
if (tty->fd < 0)
tty->fd = STDIN_FILENO;
if (fstat(tty->fd, &buf) == 0 &&
major(buf.st_rdev) == TTY_MAJOR &&
minor(buf.st_rdev) > 0)
{
if (tty->fd == STDIN_FILENO)
tty->fd = fcntl(STDIN_FILENO, F_DUPFD_CLOEXEC, 0);
tty->vt = minor(buf.st_rdev);
}
else
{
/* Fall back to try opening a new VT. This typically
* requires root. */
tty->fd = try_open_vt(tty, error);
}
if (tty->fd <= 0 && (!error || !*error))
{
g_set_error (error, G_IO_ERROR,
g_io_error_from_errno (errno),
"Could not open tty0: %s", strerror (errno));
return FALSE;
}
if (ioctl(tty->fd, VT_GETSTATE, &vts) == 0)
tty->starting_vt = vts.v_active;
else
tty->starting_vt = tty->vt;
if (tty->starting_vt != tty->vt)
{
if (ioctl(tty->fd, VT_ACTIVATE, tty->vt) < 0 ||
ioctl(tty->fd, VT_WAITACTIVE, tty->vt) < 0)
{
g_set_error (error, G_IO_ERROR,
g_io_error_from_errno (errno),
"Failed to switch to new vt: %s", strerror (errno));
goto err;
}
}
if (tcgetattr(tty->fd, &tty->terminal_attributes) < 0)
{
g_set_error (error, G_IO_ERROR,
g_io_error_from_errno (errno),
"Could not get terminal attributes: %s", strerror (errno));
goto err;
}
/* Ignore control characters and disable echo */
raw_attributes = tty->terminal_attributes;
cfmakeraw(&raw_attributes);
/* Fix up line endings to be normal (cfmakeraw hoses them) */
raw_attributes.c_oflag |= OPOST | OCRNL;
/* Don't generate ttou signals */
raw_attributes.c_oflag &= ~TOSTOP;
if (tcsetattr(tty->fd, TCSANOW, &raw_attributes) < 0)
g_warning("Could not put terminal into raw mode: %s", strerror (errno));
ioctl(tty->fd, KDGKBMODE, &tty->kb_mode);
ret = ioctl(tty->fd, KDSKBMODE, K_OFF);
if (ret)
{
ret = ioctl(tty->fd, KDSKBMODE, K_RAW);
if (ret)
{
g_set_error (error, G_IO_ERROR,
g_io_error_from_errno (errno),
"Failed to set keyboard mode: %s", strerror (errno));
goto err_attr;
}
tty->input_source = g_unix_fd_add (tty->fd,
G_IO_IN,
on_tty_input, tty);
}
ret = ioctl(tty->fd, KDSETMODE, KD_GRAPHICS);
if (ret)
{
g_set_error (error, G_IO_ERROR,
g_io_error_from_errno (errno),
"Failed to set KD_GRAPHICS mode: %s", strerror (errno));
goto err_kdkbmode;
}
mode.mode = VT_PROCESS;
mode.relsig = SIGUSR1;
mode.acqsig = SIGUSR2;
if (ioctl(tty->fd, VT_SETMODE, &mode) < 0)
{
g_set_error (error, G_IO_ERROR,
g_io_error_from_errno (errno),
"Failed to take control of vt handling: %s", strerror (errno));
goto err_kdmode;
}
tty->vt_leave_source = g_unix_signal_source_new (SIGUSR1);
g_source_set_callback (tty->vt_leave_source, vt_release_handler, tty, NULL);
tty->vt_enter_source = g_unix_signal_source_new (SIGUSR2);
g_source_set_callback (tty->vt_enter_source, quit_nested_loop, tty, NULL);
tty->nested_term = g_unix_signal_source_new (SIGTERM);
g_source_set_callback (tty->nested_term, quit_nested_loop, tty, NULL);
tty->nested_context = g_main_context_new ();
tty->nested_loop = g_main_loop_new (tty->nested_context, FALSE);
g_source_attach (tty->vt_leave_source, NULL);
g_source_attach (tty->vt_enter_source, tty->nested_context);
g_source_attach (tty->nested_term, tty->nested_context);
return TRUE;
err_kdmode:
ioctl (tty->fd, KDSETMODE, KD_TEXT);
err_kdkbmode:
if (tty->input_source)
g_source_remove (tty->input_source);
ioctl (tty->fd, KDSKBMODE, tty->kb_mode);
err_attr:
tcsetattr (tty->fd, TCSANOW, &tty->terminal_attributes);
err:
close (tty->fd);
return FALSE;
}
void
meta_tty_reset (MetaTTY *tty,
gboolean warn_if_fail)
{
struct vt_mode mode = { 0 };
if (ioctl (tty->fd, KDSKBMODE, tty->kb_mode) && warn_if_fail)
g_warning ("failed to restore keyboard mode: %s", strerror (errno));
if (ioctl (tty->fd, KDSETMODE, KD_TEXT) && warn_if_fail)
g_warning ("failed to set KD_TEXT mode on tty: %s", strerror (errno));
if (tcsetattr (tty->fd, TCSANOW, &tty->terminal_attributes) < 0 && warn_if_fail)
g_warning ("could not restore terminal to canonical mode");
mode.mode = VT_AUTO;
if (ioctl (tty->fd, VT_SETMODE, &mode) < 0 && warn_if_fail)
g_warning ("could not reset vt handling\n");
if (tty->vt != tty->starting_vt)
{
ioctl(tty->fd, VT_ACTIVATE, tty->starting_vt);
ioctl(tty->fd, VT_WAITACTIVE, tty->starting_vt);
}
}
static void
meta_tty_finalize (GObject *object)
{
MetaTTY *tty = META_TTY (object);
if (tty->input_source)
g_source_remove (tty->input_source);
g_source_destroy (tty->vt_enter_source);
g_source_destroy (tty->vt_leave_source);
g_source_destroy (tty->nested_term);
g_main_loop_unref (tty->nested_loop);
g_main_context_unref (tty->nested_context);
meta_tty_reset (tty, TRUE);
close (tty->fd);
G_OBJECT_CLASS (meta_tty_parent_class)->finalize (object);
}
static void
meta_tty_init (MetaTTY *self)
{
}
static void
meta_tty_class_init (MetaTTYClass *klass)
{
GObjectClass *object_class = G_OBJECT_CLASS (klass);
object_class->finalize = meta_tty_finalize;
signals[SIGNAL_ENTER] = g_signal_new ("enter",
G_TYPE_FROM_CLASS (klass),
G_SIGNAL_RUN_FIRST,
0, /* class offset */
NULL, NULL, /* accumulator */
g_cclosure_marshal_VOID__VOID,
G_TYPE_NONE, 0);
signals[SIGNAL_LEAVE] = g_signal_new ("leave",
G_TYPE_FROM_CLASS (klass),
G_SIGNAL_RUN_FIRST,
0, /* class offset */
NULL, NULL, /* accumulator */
g_cclosure_marshal_VOID__VOID,
G_TYPE_NONE, 0);
}
static void
meta_tty_initable_iface_init (GInitableIface *iface)
{
iface->init = meta_tty_initable_init;
}
MetaTTY *
meta_tty_new (void)
{
GError *error;
MetaTTY *tty;
error = NULL;
tty = g_initable_new (META_TYPE_TTY, NULL, &error, NULL);
if (tty == NULL)
{
g_warning ("Failed to initalize TTY handling: %s", error->message);
g_error_free (error);
}
return tty;
}

50
src/wayland/meta-tty.h Normal file
View File

@ -0,0 +1,50 @@
/*
* 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_TTY_H
#define META_TTY_H
#include <glib-object.h>
G_BEGIN_DECLS
#define META_TYPE_TTY (meta_tty_get_type())
#define META_TTY(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), META_TYPE_TTY, MetaTTY))
#define META_TTY_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), META_TYPE_TTY, MetaTTYClass))
#define META_IS_TTY(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), META_TYPE_TTY))
#define META_IS_TTY_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), META_TYPE_TTY))
#define META_TTY_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), META_TTY, MetaTTYClass))
typedef struct _MetaTTY MetaTTY;
typedef struct _MetaTTYClass MetaTTYClass;
GType meta_tty_get_type (void) G_GNUC_CONST;
MetaTTY *meta_tty_new (void);
gboolean meta_tty_activate_vt (MetaTTY *self,
int number,
GError **error);
void meta_tty_reset (MetaTTY *self,
gboolean warn_if_fail);
G_END_DECLS
#endif /* META_TTY_H */

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,618 @@
/*
* Wayland Support
*
* Copyright (C) 2013 Intel Corporation
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License as
* published by the Free Software Foundation; either version 2 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
* 02111-1307, USA.
*/
/*
* Copyright © 2010-2011 Intel Corporation
* Copyright © 2008-2011 Kristian Høgsberg
* Copyright © 2012 Collabora, Ltd.
*
* Permission to use, copy, modify, distribute, and sell this software and
* its documentation for any purpose is hereby granted without fee, provided
* that the above copyright notice appear in all copies and that both that
* copyright notice and this permission notice appear in supporting
* documentation, and that the name of the copyright holders not be used in
* advertising or publicity pertaining to distribution of the software
* without specific, written prior permission. The copyright holders make
* no representations about the suitability of this software for any
* purpose. It is provided "as is" without express or implied warranty.
*
* THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS
* SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
* FITNESS, IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY
* SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER
* RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF
* CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
* CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
/* The file is based on src/input.c from Weston */
#define _GNU_SOURCE
#include "config.h"
#include <glib.h>
#include <string.h>
#include <errno.h>
#include <stdlib.h>
#include <fcntl.h>
#include <unistd.h>
#include <sys/mman.h>
#include <clutter/evdev/clutter-evdev.h>
#include "meta-wayland-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,
mods_depressed,
mods_latched,
mods_locked,
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,
gboolean is_evdev)
{
ClutterDeviceManager *manager;
memset (keyboard, 0, sizeof *keyboard);
wl_list_init (&keyboard->resource_list);
wl_array_init (&keyboard->keys);
keyboard->focus_listener.notify = lose_keyboard_focus;
keyboard->default_grab.interface = &default_keyboard_grab_interface;
keyboard->default_grab.keyboard = keyboard;
keyboard->grab = &keyboard->default_grab;
wl_signal_init (&keyboard->focus_signal);
keyboard->display = display;
keyboard->xkb_context = xkb_context_new (0 /* flags */);
meta_wayland_keyboard_build_global_keymap (keyboard->xkb_context,
&keyboard->xkb_names,
&keyboard->xkb_info);
keyboard->is_evdev = is_evdev;
if (is_evdev)
{
manager = clutter_device_manager_get_default ();
clutter_evdev_set_keyboard_map (manager, keyboard->xkb_info.keymap);
keyboard->xkb_state = clutter_evdev_get_keyboard_state (manager);
xkb_state_ref (keyboard->xkb_state);
}
else
{
keyboard->xkb_state = xkb_state_new (keyboard->xkb_info.keymap);
}
return TRUE;
}
static void
meta_wayland_xkb_info_destroy (MetaWaylandXkbInfo *xkb_info)
{
if (xkb_info->keymap)
xkb_map_unref (xkb_info->keymap);
if (xkb_info->keymap_area)
munmap (xkb_info->keymap_area, xkb_info->keymap_size);
if (xkb_info->keymap_fd >= 0)
close (xkb_info->keymap_fd);
}
static void
update_state_from_clutter (MetaWaylandKeyboard *keyboard,
ClutterModifierType modifier_state)
{
uint32_t depressed_mods = 0;
uint32_t locked_mods = 0;
if ((modifier_state & CLUTTER_SHIFT_MASK) &&
keyboard->xkb_info.shift_mod != XKB_MOD_INVALID)
depressed_mods |= (1 << keyboard->xkb_info.shift_mod);
if ((modifier_state & CLUTTER_LOCK_MASK) &&
keyboard->xkb_info.caps_mod != XKB_MOD_INVALID)
locked_mods |= (1 << keyboard->xkb_info.caps_mod);
if ((modifier_state & CLUTTER_CONTROL_MASK) &&
keyboard->xkb_info.ctrl_mod != XKB_MOD_INVALID)
depressed_mods |= (1 << keyboard->xkb_info.ctrl_mod);
if ((modifier_state & CLUTTER_MOD1_MASK) &&
keyboard->xkb_info.alt_mod != XKB_MOD_INVALID)
depressed_mods |= (1 << keyboard->xkb_info.alt_mod);
if ((modifier_state & CLUTTER_MOD2_MASK) &&
keyboard->xkb_info.mod2_mod != XKB_MOD_INVALID)
depressed_mods |= (1 << keyboard->xkb_info.mod2_mod);
if ((modifier_state & CLUTTER_MOD3_MASK) &&
keyboard->xkb_info.mod3_mod != XKB_MOD_INVALID)
depressed_mods |= (1 << keyboard->xkb_info.mod3_mod);
if ((modifier_state & CLUTTER_SUPER_MASK) &&
keyboard->xkb_info.super_mod != XKB_MOD_INVALID)
depressed_mods |= (1 << keyboard->xkb_info.super_mod);
if ((modifier_state & CLUTTER_MOD5_MASK) &&
keyboard->xkb_info.mod5_mod != XKB_MOD_INVALID)
depressed_mods |= (1 << keyboard->xkb_info.mod5_mod);
xkb_state_update_mask (keyboard->xkb_state,
depressed_mods,
0,
locked_mods,
0, 0, 0);
}
static gboolean
state_equal (MetaWaylandXkbState *one,
MetaWaylandXkbState *two)
{
return one->mods_depressed == two->mods_depressed &&
one->mods_latched == two->mods_latched &&
one->mods_locked == two->mods_locked &&
one->group == two->group;
}
static void
set_modifiers (MetaWaylandKeyboard *keyboard,
guint32 serial,
ClutterModifierType modifier_state)
{
MetaWaylandKeyboardGrab *grab = keyboard->grab;
MetaWaylandXkbState new_state;
/* In the evdev case, the state is shared with the clutter backend, so
we don't need to update it */
if (!keyboard->is_evdev)
update_state_from_clutter (keyboard, modifier_state);
new_state.mods_depressed = xkb_state_serialize_mods (keyboard->xkb_state,
XKB_STATE_MODS_DEPRESSED);
new_state.mods_latched = xkb_state_serialize_mods (keyboard->xkb_state,
XKB_STATE_MODS_LATCHED);
new_state.mods_locked = xkb_state_serialize_mods (keyboard->xkb_state,
XKB_STATE_MODS_LOCKED);
new_state.group = xkb_state_serialize_layout (keyboard->xkb_state,
XKB_STATE_LAYOUT_EFFECTIVE);
if (state_equal (&keyboard->modifier_state, &new_state))
return;
keyboard->modifier_state = new_state;
grab->interface->modifiers (grab,
serial,
new_state.mods_depressed,
new_state.mods_latched,
new_state.mods_locked,
new_state.group);
}
void
meta_wayland_keyboard_handle_event (MetaWaylandKeyboard *keyboard,
const ClutterKeyEvent *event)
{
gboolean state = event->type == CLUTTER_KEY_PRESS;
guint evdev_code;
uint32_t serial;
/* We can't do anything with the event if we can't get an evdev
keycode for it */
if (event->device == NULL ||
!clutter_input_device_keycode_to_evdev (event->device,
event->hardware_keycode,
&evdev_code))
return;
/* We want to ignore events that are sent because of auto-repeat. In
the Clutter event stream these appear as a single key press
event. We can detect that because the key will already have been
pressed */
if (state)
{
uint32_t *end = (void *) ((char *) keyboard->keys.data +
keyboard->keys.size);
uint32_t *k;
/* Ignore the event if the key is already down */
for (k = keyboard->keys.data; k < end; k++)
if (*k == evdev_code)
return;
/* Otherwise add the key to the list of pressed keys */
k = wl_array_add (&keyboard->keys, sizeof (*k));
*k = evdev_code;
}
else
{
uint32_t *end = (void *) ((char *) keyboard->keys.data +
keyboard->keys.size);
uint32_t *k;
/* Remove the key from the array */
for (k = keyboard->keys.data; k < end; k++)
if (*k == evdev_code)
{
*k = *(end - 1);
keyboard->keys.size -= sizeof (*k);
goto found;
}
g_warning ("unexpected key release event for key 0x%x (%d)", evdev_code, event->keyval);
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->modifier_state.mods_depressed,
keyboard->modifier_state.mods_latched,
keyboard->modifier_state.mods_locked,
keyboard->modifier_state.group);
wl_keyboard_send_enter (resource, serial, surface->resource,
&keyboard->keys);
wl_resource_add_destroy_listener (resource, &keyboard->focus_listener);
keyboard->focus_serial = serial;
}
keyboard->focus_resource = resource;
keyboard->focus = surface;
wl_signal_emit (&keyboard->focus_signal, keyboard);
}
void
meta_wayland_keyboard_start_grab (MetaWaylandKeyboard *keyboard,
MetaWaylandKeyboardGrab *grab)
{
keyboard->grab = grab;
grab->keyboard = keyboard;
/* XXX focus? */
}
void
meta_wayland_keyboard_end_grab (MetaWaylandKeyboard *keyboard)
{
keyboard->grab = &keyboard->default_grab;
}
void
meta_wayland_keyboard_release (MetaWaylandKeyboard *keyboard)
{
g_free ((char *) keyboard->xkb_names.rules);
g_free ((char *) keyboard->xkb_names.model);
g_free ((char *) keyboard->xkb_names.layout);
g_free ((char *) keyboard->xkb_names.variant);
g_free ((char *) keyboard->xkb_names.options);
meta_wayland_xkb_info_destroy (&keyboard->xkb_info);
xkb_context_unref (keyboard->xkb_context);
xkb_state_unref (keyboard->xkb_state);
/* XXX: What about keyboard->resource_list? */
if (keyboard->focus_resource)
wl_list_remove (&keyboard->focus_listener.link);
wl_array_release (&keyboard->keys);
}
static void
modal_key (MetaWaylandKeyboardGrab *grab,
uint32_t time,
uint32_t key,
uint32_t state)
{
}
static void
modal_modifiers (MetaWaylandKeyboardGrab *grab,
uint32_t serial,
uint32_t mods_depressed,
uint32_t mods_latched,
uint32_t mods_locked,
uint32_t group)
{
}
static MetaWaylandKeyboardGrabInterface modal_grab = {
modal_key,
modal_modifiers,
};
gboolean
meta_wayland_keyboard_begin_modal (MetaWaylandKeyboard *keyboard)
{
MetaWaylandKeyboardGrab *grab;
if (keyboard->grab != &keyboard->default_grab)
return FALSE;
grab = g_slice_new0 (MetaWaylandKeyboardGrab);
grab->interface = &modal_grab;
meta_wayland_keyboard_start_grab (keyboard, grab);
return TRUE;
}
void
meta_wayland_keyboard_end_modal (MetaWaylandKeyboard *keyboard)
{
MetaWaylandKeyboardGrab *grab;
grab = keyboard->grab;
g_assert (grab->interface == &modal_grab);
meta_wayland_keyboard_end_grab (keyboard);
g_slice_free (MetaWaylandKeyboardGrab, grab);
}

View File

@ -0,0 +1,81 @@
/*
* 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,
gboolean is_evdev);
void
meta_wayland_keyboard_handle_event (MetaWaylandKeyboard *keyboard,
const ClutterKeyEvent *event);
void
meta_wayland_keyboard_set_focus (MetaWaylandKeyboard *keyboard,
MetaWaylandSurface *surface);
void
meta_wayland_keyboard_start_grab (MetaWaylandKeyboard *device,
MetaWaylandKeyboardGrab *grab);
void
meta_wayland_keyboard_end_grab (MetaWaylandKeyboard *keyboard);
gboolean
meta_wayland_keyboard_begin_modal (MetaWaylandKeyboard *keyboard);
void
meta_wayland_keyboard_end_modal (MetaWaylandKeyboard *keyboard);
void
meta_wayland_keyboard_release (MetaWaylandKeyboard *keyboard);
#endif /* __META_WAYLAND_KEYBOARD_H__ */

Some files were not shown because too many files have changed in this diff Show More