Compare commits

...

62 Commits

Author SHA1 Message Date
Carlos Garnacho
9f35b5816a wayland: Implement gtk-shell v3
Implement the gtk_shell.set_startup_id request, so that the ID is
removed from the sequences list, and feedback updated accordingly.
2016-02-17 14:47:54 +01:00
Carlos Garnacho
63c91f9d4f wayland: Update gtk-shell protocol file to v3
Add a gtk_shell.set_startup_id request, so the application can communicate
to the compositor the startup id that it received through the
DESKTOP_STARTUP_ID envvar, or other means.
2016-02-17 13:22:29 +01:00
Carlos Garnacho
aaa02440be core: Refactor startup notification into a separate object
This is kind of in a middle ground at the moment. Even though it
handles sequences not coming from libsn, they're added nowhere at
the moment, we'll rely on the app launch context being in the x11
side at the moment.

Also, even though we do create internal sequence objects, we keep
exposing SnStartupSequences to make gnome-shell happy, we could
consider making this object "public" (and the sequence objects with
it), things stay private at the moment.
2016-02-17 13:22:29 +01:00
Jonas Ådahl
9611661154 native: Don't wait for a new input event to wrap the pointer
If we rely on getting back an input event with the warped pointer
coordinates, we might draw a frame with the old coordinates if we warp
during the paint phase. Avoid that by moving the cursor immediately.

https://bugzilla.gnome.org/show_bug.cgi?id=744104
2016-02-16 19:02:48 +08:00
Jonas Ådahl
a70a2c3744 MetaPointerConfinementWayland: Support non-rectangular confinement regions
This patch adds support for confinement regions that are more complex
than a single rectangle. It relies on details about cairo regions not
explicitly in the API in order to generate the outer border of the
region.

https://bugzilla.gnome.org/show_bug.cgi?id=744104
2016-02-16 19:02:48 +08:00
Jonas Ådahl
bc47b19c3f wayland: Use the event coordinates when sending pointer motion events
The x/y coordinates of the ClutterInputDevice were not the ones which was
the result of this event but whatever event was queued the last. The
correct coordinates can, however, be found in the event itself, so lets
use those.

https://bugzilla.gnome.org/show_bug.cgi?id=744104
2016-02-16 19:02:48 +08:00
Jonas Ådahl
bc1dd1cee4 MetaBorder: Use float constants and functions instead of double variants
We calculate with floats, so lets use that type throughout.

https://bugzilla.gnome.org/show_bug.cgi?id=744104
2016-02-16 19:02:48 +08:00
Jonas Ådahl
495c89401a Implement support for the wp_pointer_constraints protocol
The wp_pointer_constraints protocol is a protocol which enables clients
to manipulate the behavior of the pointer cursor associated with a seat.

Currently available constraints are locking the pointer to a static
position, and confining the pointer to a given region.

Currently locking is fully implemented, and confining is implemented for
rectangular confinement regions.

What else is lacking is less troublesome semantics for enabling the lock
or confinement; currently the only requirement implemented is that the
window that appears focused is the one that may aquire the lock.

This means that a pointer could be 'stolen' by creating a new window that
receives active focus, or when using focus-follows-mouse, a pointer
passes a window that has requested a lock. This semantics can be changed
and the protocol itself allows any semantics as seems fit.

https://bugzilla.gnome.org/show_bug.cgi?id=744104
2016-02-16 19:02:48 +08:00
Jonas Ådahl
e2efc85b08 wayland: Make the pending surface state a GObject
Making the pending state an GObject makes it easier to extend it with
additional optional state without putting everything inside one big
struct.

https://bugzilla.gnome.org/show_bug.cgi?id=744104
2016-02-16 19:02:48 +08:00
Jonas Ådahl
020ae58fe4 wayland: Add "painting" signal to surface actor
Make MetaWaylandSurface a listener and move output state updating to
the handler function.

https://bugzilla.gnome.org/show_bug.cgi?id=744104
2016-02-16 19:02:48 +08:00
Jonas Ådahl
525644059d native: Update to new constrain callback API
https://bugzilla.gnome.org/show_bug.cgi?id=744104
2016-02-16 19:02:48 +08:00
Jonas Ådahl
f0f638d2bd Move out generic math parts out of the native barrier implementation
In order to reuse some vector math for pointer confinement, move out
those parts to its own file, introducing the types old types
"MetaVector2" and "MetaLine2" outside of meta-barrier-native.c, as well
as introducing MetaBorder which is a line, with a blocking direction.

https://bugzilla.gnome.org/show_bug.cgi?id=744104
2016-02-16 19:02:48 +08:00
Jonas Ådahl
bc8ec2d90d wayland: Add global to surface coordinate helper
https://bugzilla.gnome.org/show_bug.cgi?id=744104
2016-02-16 19:02:47 +08:00
Jonas Ådahl
5b0eabec51 wayland: Implement support for wp_relative_pointer
Add support for sending relative pointer motion deltas to clients who
request such events by creating wp_relative_pointer objects via
wp_relative_pointer_manager.

This currently implements the unstable version 1 from wayland-protocols.

https://bugzilla.gnome.org/show_bug.cgi?id=744104
2016-02-16 19:02:47 +08:00
Ray Strode
50099c4c10 wayland: use glib function for fetching timestamp
The code currently implements a function, get_time, that
fetches a timestamp.  That duplicates code already in glib,
and the glib implementation is better, anyway, since it doesn't
skew backward when the system clock is changed.

This commit changes the code to use g_get_monotonic_time and
drop the get_time function.

https://bugzilla.gnome.org/show_bug.cgi?id=761613
2016-02-08 17:01:58 -05:00
Rui Matos
55eef2deb3 cursor-renderer-native: Re-use cogl's gbm device
Instancing a gbm device without initializing EGL with it means that it
won't be able to import wl_drm buffers. Instead, let's re-use cogl's
gbm device which is already properly initialized.

https://bugzilla.gnome.org/show_bug.cgi?id=761557
2016-02-04 18:22:05 +01:00
Florian Müllner
f9db65f47f theme: Take invisible borders required by the theme into account
GTK+ paints some elements like box shadows (which Adwaita likes to (ab)use
for borders) outside the rectangle passed to gtk_render_*. This is not
an issue if our own invisible frame border is big enough, but in case
of non-resizable windows we end up clipping away part of the decoration.
Use the newly added gtk_render_background_get_clip() to make sure we
always use a mask that is large enough to contain all decorations.

https://bugzilla.gnome.org/show_bug.cgi?id=752794
2016-02-04 15:16:26 +01:00
Florian Müllner
a809055470 theme: Update style context hierarchy (again)
GtkWindow actually uses two CSS nodes, 'window' and 'decoration'.
Simulate that by using two separate style contexts for the frame.
2016-02-04 15:13:23 +01:00
Alberts Muktupāvels
247909e161 frames: don't force dark theme to all windows
Use global theme variant only if window does not have _GTK_THEME_VARIANT
property. This allows applications to request default theme variant when
global dark theme is enabled.

https://bugzilla.gnome.org/show_bug.cgi?id=761543
2016-02-04 16:02:21 +02:00
Alberts Muktupāvels
e5ce6192f4 frames: default theme variant now is set as empty string
Related change in GTK+:
https://git.gnome.org/browse/gtk+/commit/?id=8eb261988869608604c78ed90de5579beb4ef2b0

https://bugzilla.gnome.org/show_bug.cgi?id=761543
2016-02-04 15:54:26 +02:00
Ray Strode
7adbb58736 wayland: don't prematurely release EGL buffers
commit 0165cb6974 changed
mutter to release committed shm buffers as soon as they were
uploaded to the GPU.

It also inadvertently changed mutter to prematurely
release EGL buffers (which never get copied, but get used
directly).

This commit corrects that mistake.

https://bugzilla.gnome.org/show_bug.cgi?id=761312
2016-02-02 11:15:43 -05:00
Ray Strode
0165cb6974 wayland: release buffer after processing commit
When a client is ready for the compositor to read a surface's
shared memory buffer, it tells the compositor via
wl_surface_commit.

From that point forward, the baton is given to the compositor:
it knows it can read the buffer without worring about the client
making changes out from under it.

After the compositor has uploaded the pixel contents to the video
card it is supposed to release the buffer back to the client so that
the client can reuse it for future use.

At the moment, mutter only releases the buffer when a new buffer
is attached.  This is problematic, since it means the client has
to have a second buffer prepared before the compositor gives the
first one back.  Preparing the second buffer potentially involves
copying megabytes of pixel data, so that's suboptimal, and there's
no reason mutter couldn't release the buffer earlier.

This commit changes mutter to release a surface's buffer as soon
as it's done processing the commit request.

https://bugzilla.gnome.org/show_bug.cgi?id=761312
2016-02-01 14:16:17 -05:00
Rui Matos
3cdcd3e9c1 meta-launcher: Use g_auto* macros
This fixes a couple of minor memory leaks.

https://bugzilla.gnome.org/show_bug.cgi?id=760670
2016-01-25 13:59:53 +01:00
Jasper St. Pierre
6fc51e3723 window-x11: Fix checks for a bounding region input region
When cleaning up the logic in commit c408cf7, I forgot to properly apply
de Morgan's laws to an inverse.

Reported by ricotz on IRC.
2016-01-22 18:28:47 -08:00
Florian Müllner
af45a50cb1 Bump version to 3.19.4
Update NEWS.
2016-01-21 23:04:22 +01:00
Florian Müllner
72d6efc0d5 Shut up some compiler warnings 2016-01-21 23:04:22 +01:00
Florian Müllner
e0ffef06dd theme: Support margins on titlebar title/buttons
GTK+ improved its CSS support, and the default theme started to make
use of it, so we must update our theming code accordingly. Add support
for margins where they make sense.
2016-01-21 23:04:22 +01:00
Florian Müllner
d5b69bcd54 theme: Consider minimum sizes
GTK+ improved its CSS support, and the default theme started to make
use of it, so we must update our theming code accordingly. Start by
supporting min-width/min-height where it makes sense.
2016-01-21 23:04:22 +01:00
Rui Matos
ac8fe2d9b2 cursor-tracker: Port to GdkSeat API 2016-01-21 14:07:50 +01:00
Marek Chalupa
e7a88dc6b2 meta-launcher: don't call g_object_unref() on NULL
g_object_unref() was called no matter what we got for value

https://bugzilla.gnome.org/show_bug.cgi?id=760670
2016-01-21 14:06:41 +01:00
Jonas Ådahl
bcdda506e1 MetaWaylandDataDevice: Don't set surface offset as anchor offset
Since we are using the surface actor to draw the DND icon, the offset
is already accounted for by MetaSurfaceActorWayland, and passing the
surface position offset would effectively double the actual offset,
causing the icon to be misplaced.

This patch always sets the anchor offset to (0, 0) when the icon is a
Wayland surface, and lets the surface actor deal with the offsetting.

https://bugzilla.gnome.org/show_bug.cgi?id=759222
2016-01-19 14:32:34 +01:00
Carlos Garnacho
9b26694bbc wayland: Implement DnD actions as per wl_data_device v3 changes
We now additionally send:
  - wl_data_offer.source_actions
  - wl_data_source.action
  - wl_data_offer.action
  - wl_data_source.dnd_drop_performed
  - wl_data_source.dnd_finished

The protocol changes allow for compositors to implement different policies
when chosing the action, mutter uses this to reimplement the same behavior
that GTK+ traditionally had:

  - Alt/Control/Shift modifiers change the chosen action to
    ask/copy/move respectively
  - Drags with middle button start out as "ask" by default

As mutter now also grabs the keyboard and unsets the window focus for these
purposes, the window focus is restored after the drag operation has
finished.

The Xdnd bridge code is also modified to cope with actions, so mixed
wayland-x11 scenarios are able to convey that information.

https://bugzilla.gnome.org/show_bug.cgi?id=760805
2016-01-19 13:55:33 +01:00
Carlos Garnacho
f053c09083 data-device: Refactor data source management by the drag grab
Move to a separate meta_wayland_drag_grab_set_source() so we keep
the weak pointer management in a single place.

https://bugzilla.gnome.org/show_bug.cgi?id=760805
2016-01-19 13:45:56 +01:00
Carlos Garnacho
ec9abaf1ef wayland: Add MetaWaylandKeyboardGrab and keyboard grab API
This will be useful during DnD, where mutter is expected to consume
keyboard events for either allowing changes in the selected DnD action,
or misc a11y features like keyboard-driven DnD.

Currently, the vtable contains 2 functions, key() will be used on every
key event we get from Clutter, modifiers() will notify of changes in the
keyboard modifiers (mouse buttons will never be set in the modifier mask)

https://bugzilla.gnome.org/show_bug.cgi?id=760805
2016-01-19 13:45:56 +01:00
Carlos Garnacho
6b88420465 wayland: Add "update" vfunc to MetaWaylandDragDestFuncs
This will be useful when an update is due but no motion event is to be
sent/received (eg. modifier changes during DnD).

https://bugzilla.gnome.org/show_bug.cgi?id=760805
2016-01-19 13:45:56 +01:00
Carlos Garnacho
fc0a834abb wayland: Emit wl_pointer.frame after .enter in pointer resource creation
This place was missing concordance with wl_pointer v5.
2016-01-19 11:51:36 +01:00
Carlos Garnacho
e30010b9f0 wayland: Rename meta_wayland_pointer_send_frame() to broadcast_frame
It's closer to what we mean here. And we can have a send_frame() helper
that does this for a single wl_resource.
2016-01-19 11:50:27 +01:00
Carlos Garnacho
935d76ba04 wayland: Implement wl_pointer.axis_source/axis_stop/axis_frame emission
As per the spec:
- wl_pointer.axis_source determines the current source of
  scroll events.
- wl_pointer.axis_stop determines when there's no further
  scroll events on the given axis.
- wl_pointer.axis_discrete is emitted on "wheel"
  scroll sources, measured in ticks.
- wl_pointer.frame is meant to coalesce events that logically belong
  together, e.g. axis events in this case.

Co-Authored-By: Peter Hutterer <peter.hutterer@who-t.net>

https://bugzilla.gnome.org/show_bug.cgi?id=760637
2016-01-14 19:27:48 +01:00
Aurimas Černius
cc013e1daa Updated Lithuanian translation 2016-01-12 22:35:05 +02:00
Sebastian Keller
a7a376ae1f xprops: Null-terminate property reply values
Some of the mutter code using these properties expects them to be
null-terminated whereas xcb does not use null-terminated strings:

http://xcb.freedesktop.org/XcbRationale/

This was in some cases resulting in the WM_CLASS property containing
garbage data which broke application matching, caused the hot-corner and
window-switcher to stop working, or was exposed as text in the UI.

https://bugzilla.gnome.org/show_bug.cgi?id=759658
2016-01-12 16:30:30 +01:00
Rui Matos
5e57af6286 idle-monitor-native: Don't leak user active watches
This fixes an issue analogous to bug 760330 for the X11 backend,
except on this backend we wouldn't crash accessing free'd memory.

Instead we're leaking watches since we steal them from the hash table
which means that when they're removed in
_meta_idle_monitor_watch_fire() they're no longer there and thus
they're never free'd.

https://bugzilla.gnome.org/show_bug.cgi?id=760476
2016-01-11 11:23:54 -05:00
Ray Strode
42b3a34f7b idle-monitor-xsync: fix crash if watch callback removes different watch
Right now the XSync based idle monitoring code, will fetch all active
watches into a list, and then call their watch callbacks one by one
as necessary.  If one watch callback invalidates another watch, the
list will contain free'd memory.

This commit makes sure to consult the hash table after ever call
of a watch callback, to ensure mutter never looks at freed memory.

Fixes crash reported on IRC by Laine Stump with his synergy setup.

https://bugzilla.gnome.org/show_bug.cgi?id=760330
2016-01-11 11:23:39 -05:00
Rui Matos
19d814c887 cursor-renderer-native: Avoid a crash if we fail to allocate a gbm bo
https://bugzilla.gnome.org/show_bug.cgi?id=758613
2016-01-11 14:31:42 +01:00
Florian Müllner
46eb682c83 Revert "window: Remove old tiling code"
This reverts commit 8bded7d497.
2016-01-10 15:16:09 +01:00
Florian Müllner
3aea8d8ce6 Revert "Add "size states" which save window size information"
This reverts commit 2c7ef2269f.
2016-01-10 15:16:09 +01:00
Florian Müllner
40c3c69435 Revert "window: Add new tiling code"
This reverts commit 50e3e3b929.
2016-01-10 15:16:08 +01:00
Florian Müllner
8071e5b149 Revert "frame: Don't allow resizing of edges that are constrained"
This reverts commit 8a481b3e10.
2016-01-10 15:16:06 +01:00
Jasper St. Pierre
8a481b3e10 frame: Don't allow resizing of edges that are constrained
https://bugzilla.gnome.org/show_bug.cgi?id=751857
2016-01-09 18:11:15 -08:00
Jasper St. Pierre
50e3e3b929 window: Add new tiling code
The new tiling code, instead of based around "tiling states", is instead
based around constrained edges. This allows us to have windows that have
three constrained edges, but keep one free-floating, e.g. a window tiled
to the left has the left, top, and bottom edges constrained, but the
right edge can be left resizable.

This system also is easily extended to support corner tiling. We also,
using the new "size state" system, also keep normal, tiled, and
maximized sizes independently, allowing the maximize button to bounce
between maximized and tiled states without reverting to normal in
between. Dragging from the top will always restore the normal state,
though.

https://bugzilla.gnome.org/show_bug.cgi?id=751857
2016-01-09 18:09:30 -08:00
Jasper St. Pierre
2c7ef2269f Add "size states" which save window size information
https://bugzilla.gnome.org/show_bug.cgi?id=751857
2016-01-09 18:08:35 -08:00
Jasper St. Pierre
8bded7d497 window: Remove old tiling code
We'll soon replace this with a better scheme

https://bugzilla.gnome.org/show_bug.cgi?id=751857
2016-01-09 18:08:32 -08:00
Jasper St. Pierre
9ebe3419c3 monitor-manager-xrandr: Don't query legacy properties
This never could have worked -- the Atom name it was querying was a root
window property, not an output property.
2016-01-09 18:06:18 -08:00
Rui Matos
9385c835b8 native: Don't leak DRM plane resources 2016-01-07 19:42:19 +01:00
Carlos Garnacho
efef0c993b native: Implement DRM-based crtc rotation
We can know the rotation modes supported by the driver, so
export these as our supported modes, and ensure these modes
are honored on the CRTC primary plane upon apply_configuration().

It is worth noting however that not all hardware will be
capable of supporting all rotation modes (in fact, most of
them won't). A driver independent solution should be in
place to back up the rotation modes unsupported by the
drivers, so this is still a partial solution.

The cursor renderer has also been changed to default to
software-based rendering anytime the cursor enters a
rotated CRTC. Another solution would be actually rotating
the DRM cursor planes, but then it requires applying rotation on
these per-CRTC, and actually transforming the pointer position by
the output matrix. This brings marginal gains, so we use the
"sw" rendered cursor, which will be transformed together with
the primary plane.

https://bugzilla.gnome.org/show_bug.cgi?id=745079
2016-01-07 17:03:44 +01:00
Rui Matos
3a2cd3389a window: Re-evaluate window visibility when making it transient
In case a window is hidden when we're ordered to make it transient to
a different parent we must re-evaluate its visibility status or we'll
get into an inconsistent state where the parent is visible and the
child isn't.

https://bugzilla.gnome.org/show_bug.cgi?id=759297
2016-01-06 18:56:07 +01:00
Rui Matos
88acfb8e60 window: Focus windows on button press regardless of modifier state
This seems like a more generally useful and intuitive behavior. Note
that, in X sessions, this is what already happened in practice since
meta_display_begin_grab_op() calls meta_window_grab_all_keys() which,
on X11, does meta_window_focus().

https://bugzilla.gnome.org/show_bug.cgi?id=756789
2016-01-05 20:42:05 +01:00
Rui Matos
8e22dce5d7 window: Remove a spurious meta_verbose()
This was duplicated here by mistake in the big re-work commit
7a109a18af .

https://bugzilla.gnome.org/show_bug.cgi?id=756789
2016-01-05 20:42:04 +01:00
Rui Matos
e0906a77aa ui/frames: Don't focus if the button press is on the client area
This is a really old behavior introduced in commit
585e362526 which is inconsistent since
it only applies to SSD windows.

If we really want this, we should focus the window elsewhere so that
it applies consistently to all windows.

https://bugzilla.gnome.org/show_bug.cgi?id=756789
2016-01-05 20:42:02 +01:00
Jasper St. Pierre
053f5088df window-x11: Fix unredirection for windows that explicitly set bounding
Some applications, like Chromium, explicitly set their bounding region
to the client area when full-screen. Detect this case, and allow us to
fullscreen when this happens.
2016-01-04 08:32:09 -08:00
Jonas Ådahl
75b992c7d0 tests: Add test for testing that setting a parent affects the stack
A new test is added that tests that xdg_surface.set_parent (referred to
as "transient for" in X11 terminology) affects the stack immediately.

https://bugzilla.gnome.org/show_bug.cgi?id=755606
2015-12-23 14:59:36 +08:00
Jonas Ådahl
213f0fa160 window: Update the stack after setting the transient_for field
Don't update the stack until after setting the window->transient_for
field. Updating before will cause the stack transient-for constraint to
be missing until the next time constraints are applied.

https://bugzilla.gnome.org/show_bug.cgi?id=755606
2015-12-23 14:59:36 +08:00
Jonas Ådahl
5054b2a99c tests: Don't rely on latency for actually showing Wayland windows
The test runner sends a "show" command to the test clients and assumes
this was enough work done by the client to enable the compositor to map
the window. Now that we wait to show a Wayland window until the first
buffer is attached (see bug 750552), we need to make sure that we attach
a buffer before assuming that we have the final stacking order.

So, to in order to continue relying on "show" to be enough to actually
show a window, let the test client wait until it has drawn the first
frame.

This makes the tests using Wayland clients test non-flaky.

https://bugzilla.gnome.org/show_bug.cgi?id=754711
2015-12-23 12:19:36 +08:00
63 changed files with 5040 additions and 831 deletions

4
.gitignore vendored
View File

@@ -68,6 +68,10 @@ src/xdg-shell-unstable-v*-protocol.c
src/xdg-shell-unstable-v*-server-protocol.h
src/pointer-gestures-unstable-v*-protocol.c
src/pointer-gestures-unstable-v*-server-protocol.h
src/relative-pointer-unstable-v*-protocol.c
src/relative-pointer-unstable-v*-server-protocol.h
src/pointer-constraints-unstable-v*-protocol.c
src/pointer-constraints-unstable-v*-server-protocol.h
src/meta/meta-version.h
doc/reference/*.args
doc/reference/*.bak

17
NEWS
View File

@@ -1,3 +1,20 @@
3.19.4
======
* Fix updating stacking order when setting transient_for [Jonas; #755606]
* Support screen rotation when supported by the driver [Carlos; #745079]
* Protect against broken WM_CLASS property implementations [Sebastian; #759658]
* Handle wl_pointer v5 events on wayland [Carlos; #760637]
* Implement DND actions on wayland [Carlos; #760805]
* Misc. bug fixes [Jonas, Rui, Ray, Marek; #754711, #756789, #759297, #758613,
#760330, #760476, #759222, #760670]
Contributors:
Jonas Ådahl, Marek Chalupa, Carlos Garnacho, Sebastian Keller, Rui Matos,
Florian Müllner, Jasper St. Pierre, Ray Strode
Translations:
Aurimas Černius [lt]
3.19.3
======
* Correct refresh rate units on KMS/Wayland [Daniel; #758653]

View File

@@ -2,7 +2,7 @@ AC_PREREQ(2.62)
m4_define([mutter_major_version], [3])
m4_define([mutter_minor_version], [19])
m4_define([mutter_micro_version], [3])
m4_define([mutter_micro_version], [4])
m4_define([mutter_version],
[mutter_major_version.mutter_minor_version.mutter_micro_version])
@@ -59,7 +59,7 @@ CANBERRA_GTK_VERSION=0.26
CLUTTER_PACKAGE=clutter-1.0
MUTTER_PC_MODULES="
gtk+-3.0 >= 3.19.1
gtk+-3.0 >= 3.19.8
gio-unix-2.0 >= 2.35.1
pango >= 1.2.0
cairo >= 1.10.0
@@ -221,7 +221,7 @@ AS_IF([test "$have_wayland" = "yes"], [
AC_SUBST([WAYLAND_SCANNER])
AC_DEFINE([HAVE_WAYLAND],[1],[Define if you want to enable Wayland support])
PKG_CHECK_MODULES(WAYLAND_PROTOCOLS, [wayland-protocols >= 1.0],
PKG_CHECK_MODULES(WAYLAND_PROTOCOLS, [wayland-protocols >= 1.1],
[ac_wayland_protocols_pkgdatadir=`$PKG_CONFIG --variable=pkgdatadir wayland-protocols`])
AC_SUBST(WAYLAND_PROTOCOLS_DATADIR, $ac_wayland_protocols_pkgdatadir)
])

114
po/lt.po
View File

@@ -13,8 +13,8 @@ msgstr ""
"Project-Id-Version: lt\n"
"Report-Msgid-Bugs-To: http://bugzilla.gnome.org/enter_bug.cgi?"
"product=mutter&keywords=I18N+L10N&component=general\n"
"POT-Creation-Date: 2015-02-28 11:14+0000\n"
"PO-Revision-Date: 2015-02-28 21:34+0200\n"
"POT-Creation-Date: 2016-01-05 13:43+0000\n"
"PO-Revision-Date: 2016-01-05 18:04+0200\n"
"Last-Translator: Aurimas Černius <aurisc4@gmail.com>\n"
"Language-Team: Lietuvių <gnome-lt@lists.akl.lt>\n"
"Language: lt\n"
@@ -23,7 +23,7 @@ msgstr ""
"Content-Transfer-Encoding: 8bit\n"
"Plural-Forms: nplurals=3; plural=(n%10==1 && n%100!=11 ? 0 : n%10>=2 && (n"
"%100<10 || n%100>=20) ? 1 : 2);\n"
"X-Generator: Gtranslator 2.91.6\n"
"X-Generator: Poedit 1.8.6\n"
#: ../data/50-mutter-navigation.xml.in.h:1
msgid "Navigation"
@@ -31,39 +31,39 @@ msgstr "Navigacija"
#: ../data/50-mutter-navigation.xml.in.h:2
msgid "Move window to workspace 1"
msgstr "Perkelti langą į darbalaukį Nr.1"
msgstr "Perkelti langą į darbo sritį Nr.1"
#: ../data/50-mutter-navigation.xml.in.h:3
msgid "Move window to workspace 2"
msgstr "Perkelti langą į darbalaukį Nr.2"
msgstr "Perkelti langą į darbo sritį Nr.2"
#: ../data/50-mutter-navigation.xml.in.h:4
msgid "Move window to workspace 3"
msgstr "Perkelti langą į darbalaukį Nr.3"
msgstr "Perkelti langą į darbo sritį Nr.3"
#: ../data/50-mutter-navigation.xml.in.h:5
msgid "Move window to workspace 4"
msgstr "Perkelti langą į darbalaukį Nr.4"
msgstr "Perkelti langą į darbo sritį Nr.4"
#: ../data/50-mutter-navigation.xml.in.h:6
msgid "Move window to last workspace"
msgstr "Perkelti langą į pastarąjį darbalaukį"
msgstr "Perkelti langą į pastarąją darbo sritį"
#: ../data/50-mutter-navigation.xml.in.h:7
msgid "Move window one workspace to the left"
msgstr "Perkelti langą į kairiau esan darbalaukį"
msgstr "Perkelti langą į kairiau esančią darbo sritį"
#: ../data/50-mutter-navigation.xml.in.h:8
msgid "Move window one workspace to the right"
msgstr "Perkelti langą į dešiniau esan darbalaukį"
msgstr "Perkelti langą į dešiniau esančią darbo sritį"
#: ../data/50-mutter-navigation.xml.in.h:9
msgid "Move window one workspace up"
msgstr "Perkelti langą į aukščiau esan darbalaukį"
msgstr "Perkelti langą į aukščiau esančią darbo sritį"
#: ../data/50-mutter-navigation.xml.in.h:10
msgid "Move window one workspace down"
msgstr "Perkelti langą į žemiau esan darbalaukį"
msgstr "Perkelti langą į žemiau esančią darbo sritį"
#: ../data/50-mutter-navigation.xml.in.h:11
msgid "Move window one monitor to the left"
@@ -143,39 +143,39 @@ msgstr "Paslėpti visus įprastinius langus"
#: ../data/50-mutter-navigation.xml.in.h:30
msgid "Switch to workspace 1"
msgstr "Persijungti į darbalaukį Nr.1"
msgstr "Persijungti į darbo sritį Nr.1"
#: ../data/50-mutter-navigation.xml.in.h:31
msgid "Switch to workspace 2"
msgstr "Persijungti į darbalaukį Nr.2"
msgstr "Persijungti į darbo sritį Nr.2"
#: ../data/50-mutter-navigation.xml.in.h:32
msgid "Switch to workspace 3"
msgstr "Persijungti į darbalaukį Nr.3"
msgstr "Persijungti į darbo sritį Nr.3"
#: ../data/50-mutter-navigation.xml.in.h:33
msgid "Switch to workspace 4"
msgstr "Persijungti į darbalaukį Nr.4"
msgstr "Persijungti į darbo sritį Nr.4"
#: ../data/50-mutter-navigation.xml.in.h:34
msgid "Switch to last workspace"
msgstr "Persijungti į pastarąjį darbalaukį"
msgstr "Persijungti į pastarąją darbo sritį"
#: ../data/50-mutter-navigation.xml.in.h:35
msgid "Move to workspace left"
msgstr "Perkelti į darbalaukį kairėje"
msgstr "Perkelti į darbo sritį kairėje"
#: ../data/50-mutter-navigation.xml.in.h:36
msgid "Move to workspace right"
msgstr "Perkelti į darbalaukį dešinėje"
msgstr "Perkelti į darbo sritį dešinėje"
#: ../data/50-mutter-navigation.xml.in.h:37
msgid "Move to workspace above"
msgstr "Perkelti į darbalaukį viršuje"
msgstr "Perkelti į darbo sritį viršuje"
#: ../data/50-mutter-navigation.xml.in.h:38
msgid "Move to workspace below"
msgstr "Perkelti į darbalaukį apačioje"
msgstr "Perkelti į darbo sritį apačioje"
#: ../data/50-mutter-system.xml.in.h:1
msgid "System"
@@ -235,7 +235,7 @@ msgstr "Keisti lango dydį"
#: ../data/50-mutter-windows.xml.in.h:12
msgid "Toggle window on all workspaces or one"
msgstr "Perjungti lango buvimo visuose darbalaukiuose būseną"
msgstr "Perjungti lango buvimo visose darbo srityse būseną"
#: ../data/50-mutter-windows.xml.in.h:13
msgid "Raise window if covered, otherwise lower it"
@@ -272,7 +272,7 @@ msgstr "Mutter"
#: ../data/org.gnome.mutter.gschema.xml.in.h:1
msgid "Modifier to use for extended window management operations"
msgstr "Klavišas, naudojamas kartu su specialiais lango valdymo veiksmais"
msgstr "Klavišas, naudojamas kartu su specialiomis lango tvarkymo operacijomis"
#: ../data/org.gnome.mutter.gschema.xml.in.h:2
msgid ""
@@ -315,7 +315,7 @@ msgstr ""
#: ../data/org.gnome.mutter.gschema.xml.in.h:7
msgid "Workspaces are managed dynamically"
msgstr "Darbalaukiai valdomi dinamiškai"
msgstr "Darbo sritys tvarkomos dinamiškai"
#: ../data/org.gnome.mutter.gschema.xml.in.h:8
msgid ""
@@ -323,21 +323,21 @@ msgid ""
"static number of workspaces (determined by the num-workspaces key in org."
"gnome.desktop.wm.preferences)."
msgstr ""
"Nusako, ar darbastaliai yra valdomi dinamiškai, ar yra pastovus darbalaukių "
"skaičius (nusakomas raktu num-workspaces schemoje org.gnome.desktop.wm."
"preferences)."
"Nusako, ar darbo sritys yra tvarkomos dinamiškai, ar yra pastovus darbo "
"sričių skaičius (nusakomas raktu num-workspaces schemoje org.gnome.desktop."
"wm.preferences)."
#: ../data/org.gnome.mutter.gschema.xml.in.h:9
msgid "Workspaces only on primary"
msgstr "Darbalaukiai tik pagrindiniame"
msgstr "Darbo sritys tik pagrindiniame"
#: ../data/org.gnome.mutter.gschema.xml.in.h:10
msgid ""
"Determines whether workspace switching should happen for windows on all "
"monitors or only for windows on the primary monitor."
msgstr ""
"Nusako, ar darbalaukių perjungimas turi įvykti langams viusose monitoriuose, "
"ar tik langams pagrindiniame monitoriuje."
"Nusako, ar darbo sričių perjungimas turi įvykti langams visuose "
"monitoriuose, ar tik langams pagrindiniame monitoriuje."
#: ../data/org.gnome.mutter.gschema.xml.in.h:11
msgid "No tab popup"
@@ -438,46 +438,41 @@ msgid "Switch to VT 7"
msgstr "Persijungti į VT 7"
#: ../data/org.gnome.mutter.wayland.gschema.xml.in.h:8
#| msgid "Switch to VT 1"
msgid "Switch to VT 8"
msgstr "Persijungti į VT 8"
#: ../data/org.gnome.mutter.wayland.gschema.xml.in.h:9
#| msgid "Switch to VT 1"
msgid "Switch to VT 9"
msgstr "Persijungti į VT 9"
#: ../data/org.gnome.mutter.wayland.gschema.xml.in.h:10
#| msgid "Switch to VT 1"
msgid "Switch to VT 10"
msgstr "Persijungti į VT 10"
#: ../data/org.gnome.mutter.wayland.gschema.xml.in.h:11
#| msgid "Switch to VT 1"
msgid "Switch to VT 11"
msgstr "Persijungti į VT 11"
#: ../data/org.gnome.mutter.wayland.gschema.xml.in.h:12
#| msgid "Switch to VT 1"
msgid "Switch to VT 12"
msgstr "Persijungti į VT 12"
#: ../src/backends/meta-monitor-manager.c:364
#: ../src/backends/meta-monitor-manager.c:518
msgid "Built-in display"
msgstr "Integruotas vaizduoklis"
#: ../src/backends/meta-monitor-manager.c:391
#: ../src/backends/meta-monitor-manager.c:544
msgid "Unknown"
msgstr "Nežinomas"
#: ../src/backends/meta-monitor-manager.c:393
#: ../src/backends/meta-monitor-manager.c:546
msgid "Unknown Display"
msgstr "Nežinomas vaizduoklis"
#. TRANSLATORS: this is a monitor vendor name, followed by a
#. * size in inches, like 'Dell 15"'
#.
#: ../src/backends/meta-monitor-manager.c:401
#: ../src/backends/meta-monitor-manager.c:554
#, c-format
msgid "%s %s"
msgstr "%s %s"
@@ -489,7 +484,7 @@ msgstr "%s %s"
msgid ""
"Another compositing manager is already running on screen %i on display \"%s"
"\"."
msgstr "Kita kompozicijos valdyklė jau veikia ekrane %i vaizduoklyje „%s“."
msgstr "Kita kompozicijos tvarkytuvė jau veikia ekrane %i vaizduoklyje „%s“."
#: ../src/core/bell.c:185
msgid "Bell event"
@@ -518,40 +513,40 @@ msgstr "_Laukti"
msgid "_Force Quit"
msgstr "_Priverstinai išeiti"
#: ../src/core/display.c:562
#: ../src/core/display.c:563
#, c-format
msgid "Failed to open X Window System display '%s'\n"
msgstr "Nepavyko atverti X Window sistemos ekrano „%s“\n"
#: ../src/core/main.c:176
#: ../src/core/main.c:180
msgid "Disable connection to session manager"
msgstr "Išjungti susijungimą su sesijos valdykle"
msgstr "Išjungti susijungimą su sesijos tvarkytuve"
#: ../src/core/main.c:182
#: ../src/core/main.c:186
msgid "Replace the running window manager"
msgstr "Pakeisti veikiančią langų valdyklę"
msgstr "Pakeisti veikiančią langų tvarkytuvę"
#: ../src/core/main.c:188
#: ../src/core/main.c:192
msgid "Specify session management ID"
msgstr "Nurodyti sesijos valdymo ID"
msgstr "Nurodyti sesijos tvarkymo ID"
#: ../src/core/main.c:193
#: ../src/core/main.c:197
msgid "X Display to use"
msgstr "Naudotinas X ekranas"
#: ../src/core/main.c:199
#: ../src/core/main.c:203
msgid "Initialize session from savefile"
msgstr "Inicializuoti sesiją iš išsaugojimo failo"
#: ../src/core/main.c:205
#: ../src/core/main.c:209
msgid "Make X calls synchronous"
msgstr "Sinchronizuoti X iškvietimus"
#: ../src/core/main.c:212
#: ../src/core/main.c:216
msgid "Run as a wayland compositor"
msgstr "Vykdyti kaip wayland kompozitorių"
#: ../src/core/main.c:220
#: ../src/core/main.c:224
msgid "Run as a full display server, rather than nested"
msgstr "Vykdyti kaip visą vaizduoklio serverį, o ne vidinį"
@@ -580,24 +575,21 @@ msgstr "Parodyti versiją"
msgid "Mutter plugin to use"
msgstr "Naudojamas Mutter įskiepis"
#: ../src/core/prefs.c:2004
#: ../src/core/prefs.c:1997
#, c-format
msgid "Workspace %d"
msgstr "Darbalaukis %d"
msgstr "Darbo sritis %d"
#: ../src/core/screen.c:525
#: ../src/core/screen.c:526
#, c-format
#| msgid ""
#| "Screen %d on display \"%s\" already has a window manager; try using the --"
#| "replace option to replace the current window manager.\n"
msgid ""
"Display \"%s\" already has a window manager; try using the --replace option "
"to replace the current window manager."
msgstr ""
"Vaizduoklis „%s“ jau turi langų valdyklę; pabandykite pakeisti esamą langų "
"valdyklę naudodami parametrą --replace."
"Vaizduoklis „%s“ jau turi langų tvarkytuvę; pabandykite pakeisti esamą langų "
"tvarkytuvę, naudodami parametrą --replace."
#: ../src/core/screen.c:607
#: ../src/core/screen.c:608
#, c-format
msgid "Screen %d on display '%s' is invalid\n"
msgstr "Ekranas %d vaizduoklyje „%s“ netinkamas\n"

View File

@@ -9,6 +9,7 @@ dist_stacking_DATA = \
tests/stacking/basic-wayland.metatest \
tests/stacking/minimized.metatest \
tests/stacking/mixed-windows.metatest \
tests/stacking/set-parent.metatest \
tests/stacking/override-redirect.metatest
mutter-all.test: tests/mutter-all.test.in

View File

@@ -51,6 +51,10 @@ mutter_built_sources += \
gtk-shell-server-protocol.h \
xdg-shell-unstable-v5-protocol.c \
xdg-shell-unstable-v5-server-protocol.h \
relative-pointer-unstable-v1-protocol.c \
relative-pointer-unstable-v1-server-protocol.h \
pointer-constraints-unstable-v1-protocol.c \
pointer-constraints-unstable-v1-server-protocol.h \
$(NULL)
endif
@@ -84,6 +88,8 @@ libmutter_la_SOURCES = \
backends/meta-monitor-manager-private.h \
backends/meta-monitor-manager-dummy.c \
backends/meta-monitor-manager-dummy.h \
backends/meta-pointer-constraint.c \
backends/meta-pointer-constraint.h \
backends/meta-stage.h \
backends/meta-stage.c \
backends/edid-parse.c \
@@ -110,6 +116,8 @@ libmutter_la_SOURCES = \
core/boxes.c \
core/boxes-private.h \
meta/boxes.h \
core/meta-border.c \
core/meta-border.h \
compositor/clutter-utils.c \
compositor/clutter-utils.h \
compositor/cogl-utils.c \
@@ -190,6 +198,8 @@ libmutter_la_SOURCES = \
core/screen.c \
core/screen-private.h \
meta/screen.h \
core/startup-notification.c \
core/startup-notification-private.h \
meta/types.h \
core/restart.c \
core/stack.c \
@@ -264,6 +274,12 @@ libmutter_la_SOURCES += \
wayland/meta-wayland-keyboard.h \
wayland/meta-wayland-pointer.c \
wayland/meta-wayland-pointer.h \
wayland/meta-wayland-pointer-constraints.c \
wayland/meta-wayland-pointer-constraints.h \
wayland/meta-pointer-lock-wayland.c \
wayland/meta-pointer-lock-wayland.h \
wayland/meta-pointer-confinement-wayland.c \
wayland/meta-pointer-confinement-wayland.h \
wayland/meta-wayland-popup.c \
wayland/meta-wayland-popup.h \
wayland/meta-wayland-seat.c \

View File

@@ -34,6 +34,7 @@
#include <meta/meta-idle-monitor.h>
#include "meta-cursor-renderer.h"
#include "meta-monitor-manager-private.h"
#include "backends/meta-pointer-constraint.h"
#define DEFAULT_XKB_RULES_FILE "evdev"
#define DEFAULT_XKB_MODEL "pc105+inet"
@@ -51,6 +52,8 @@ struct _MetaBackend
GHashTable *device_monitors;
gint current_device_id;
MetaPointerConstraint *client_pointer_constraint;
};
struct _MetaBackendClass
@@ -87,6 +90,13 @@ struct _MetaBackendClass
void (* update_screen_size) (MetaBackend *backend, int width, int height);
void (* select_stage_events) (MetaBackend *backend);
gboolean (* get_relative_motion_deltas) (MetaBackend *backend,
const ClutterEvent *event,
double *dx,
double *dy,
double *dx_unaccel,
double *dy_unaccel);
};
MetaIdleMonitor * meta_backend_get_idle_monitor (MetaBackend *backend,
@@ -110,4 +120,14 @@ struct xkb_keymap * meta_backend_get_keymap (MetaBackend *backend);
void meta_backend_update_last_device (MetaBackend *backend,
int device_id);
gboolean meta_backend_get_relative_motion_deltas (MetaBackend *backend,
const ClutterEvent *event,
double *dx,
double *dy,
double *dx_unaccel,
double *dy_unaccel);
void meta_backend_set_client_pointer_constraint (MetaBackend *backend,
MetaPointerConstraint *constraint);
#endif /* META_BACKEND_PRIVATE_H */

View File

@@ -353,6 +353,17 @@ meta_backend_real_select_stage_events (MetaBackend *backend)
/* Do nothing */
}
static gboolean
meta_backend_real_get_relative_motion_deltas (MetaBackend *backend,
const ClutterEvent *event,
double *dx,
double *dy,
double *dx_unaccel,
double *dy_unaccel)
{
return FALSE;
}
static void
meta_backend_class_init (MetaBackendClass *klass)
{
@@ -366,6 +377,7 @@ meta_backend_class_init (MetaBackendClass *klass)
klass->ungrab_device = meta_backend_real_ungrab_device;
klass->update_screen_size = meta_backend_real_update_screen_size;
klass->select_stage_events = meta_backend_real_select_stage_events;
klass->get_relative_motion_deltas = meta_backend_real_get_relative_motion_deltas;
g_signal_new ("keymap-changed",
G_TYPE_FROM_CLASS (object_class),
@@ -544,6 +556,32 @@ meta_backend_update_last_device (MetaBackend *backend,
}
}
gboolean
meta_backend_get_relative_motion_deltas (MetaBackend *backend,
const ClutterEvent *event,
double *dx,
double *dy,
double *dx_unaccel,
double *dy_unaccel)
{
MetaBackendClass *klass = META_BACKEND_GET_CLASS (backend);
return klass->get_relative_motion_deltas (backend,
event,
dx, dy,
dx_unaccel, dy_unaccel);
}
void
meta_backend_set_client_pointer_constraint (MetaBackend *backend,
MetaPointerConstraint *constraint)
{
g_assert (!constraint || (constraint && !backend->client_pointer_constraint));
g_clear_object (&backend->client_pointer_constraint);
if (constraint)
backend->client_pointer_constraint = g_object_ref (constraint);
}
static GType
get_backend_type (void)
{

View File

@@ -26,6 +26,8 @@
#ifndef META_BARRIER_PRIVATE_H
#define META_BARRIER_PRIVATE_H
#include "core/meta-border.h"
G_BEGIN_DECLS
#define META_TYPE_BARRIER_IMPL (meta_barrier_impl_get_type ())
@@ -67,14 +69,7 @@ G_END_DECLS
struct _MetaBarrierPrivate
{
MetaDisplay *display;
int x1;
int y1;
int x2;
int y2;
MetaBarrierDirection directions;
MetaBorder border;
MetaBarrierImpl *impl;
};

View File

@@ -61,19 +61,20 @@ meta_barrier_get_property (GObject *object,
g_value_set_object (value, priv->display);
break;
case PROP_X1:
g_value_set_int (value, priv->x1);
g_value_set_int (value, priv->border.line.a.x);
break;
case PROP_Y1:
g_value_set_int (value, priv->y1);
g_value_set_int (value, priv->border.line.a.y);
break;
case PROP_X2:
g_value_set_int (value, priv->x2);
g_value_set_int (value, priv->border.line.b.x);
break;
case PROP_Y2:
g_value_set_int (value, priv->y2);
g_value_set_int (value, priv->border.line.b.y);
break;
case PROP_DIRECTIONS:
g_value_set_flags (value, priv->directions);
g_value_set_flags (value,
meta_border_get_allows_directions (&priv->border));
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
@@ -95,19 +96,20 @@ meta_barrier_set_property (GObject *object,
priv->display = g_value_get_object (value);
break;
case PROP_X1:
priv->x1 = g_value_get_int (value);
priv->border.line.a.x = g_value_get_int (value);
break;
case PROP_Y1:
priv->y1 = g_value_get_int (value);
priv->border.line.a.y = g_value_get_int (value);
break;
case PROP_X2:
priv->x2 = g_value_get_int (value);
priv->border.line.b.x = g_value_get_int (value);
break;
case PROP_Y2:
priv->y2 = g_value_get_int (value);
priv->border.line.b.y = g_value_get_int (value);
break;
case PROP_DIRECTIONS:
priv->directions = g_value_get_flags (value);
meta_border_set_allows_directions (&priv->border,
g_value_get_flags (value));
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
@@ -166,7 +168,8 @@ meta_barrier_constructed (GObject *object)
MetaBarrier *barrier = META_BARRIER (object);
MetaBarrierPrivate *priv = barrier->priv;
g_return_if_fail (priv->x1 == priv->x2 || priv->y1 == priv->y2);
g_return_if_fail (priv->border.line.a.x == priv->border.line.b.x ||
priv->border.line.a.y == priv->border.line.b.y);
#if defined(HAVE_NATIVE_BACKEND)
if (META_IS_BACKEND_NATIVE (meta_get_backend ()))

View File

@@ -361,12 +361,12 @@ get_pointer_position_gdk (int *x,
int *y,
int *mods)
{
GdkDeviceManager *gmanager;
GdkSeat *gseat;
GdkDevice *gdevice;
GdkScreen *gscreen;
gmanager = gdk_display_get_device_manager (gdk_display_get_default ());
gdevice = gdk_x11_device_manager_lookup (gmanager, META_VIRTUAL_CORE_POINTER_ID);
gseat = gdk_display_get_default_seat (gdk_display_get_default ());
gdevice = gdk_seat_get_pointer (gseat);
gdk_device_get_position (gdevice, &gscreen, x, y);
if (mods)

View File

@@ -0,0 +1,57 @@
/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
/*
* Copyright (C) 2015 Red Hat
*
* 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.
*
* Written by:
* Jonas Ådahl <jadahl@gmail.com>
*/
#include "config.h"
#include "backends/meta-pointer-constraint.h"
#include <glib-object.h>
G_DEFINE_TYPE (MetaPointerConstraint, meta_pointer_constraint, G_TYPE_OBJECT);
static void
meta_pointer_constraint_init (MetaPointerConstraint *constraint)
{
}
static void
meta_pointer_constraint_class_init (MetaPointerConstraintClass *klass)
{
}
void
meta_pointer_constraint_constrain (MetaPointerConstraint *constraint,
ClutterInputDevice *device,
guint32 time,
float prev_x,
float prev_y,
float *x,
float *y)
{
META_POINTER_CONSTRAINT_GET_CLASS (constraint)->constrain (constraint,
device,
time,
prev_x, prev_y,
x, y);
}

View File

@@ -0,0 +1,60 @@
/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
/*
* Copyright (C) 2015 Red Hat
*
* 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.
*
* Written by:
* Jonas Ådahl <jadahl@gmail.com>
*/
#ifndef META_POINTER_CONSTRAINT_H
#define META_POINTER_CONSTRAINT_H
#include <glib-object.h>
#include <clutter/clutter.h>
G_BEGIN_DECLS
#define META_TYPE_POINTER_CONSTRAINT (meta_pointer_constraint_get_type ())
G_DECLARE_DERIVABLE_TYPE (MetaPointerConstraint, meta_pointer_constraint,
META, POINTER_CONSTRAINT, GObject);
struct _MetaPointerConstraintClass
{
GObjectClass parent_class;
void (*constrain) (MetaPointerConstraint *constraint,
ClutterInputDevice *device,
guint32 time,
float prev_x,
float prev_y,
float *x,
float *y);
};
void meta_pointer_constraint_constrain (MetaPointerConstraint *constraint,
ClutterInputDevice *device,
guint32 time,
float prev_x,
float prev_y,
float *x,
float *y);
G_END_DECLS
#endif /* META_POINTER_CONSTRAINT_H */

View File

@@ -36,6 +36,8 @@
#include "meta-monitor-manager-kms.h"
#include "meta-cursor-renderer-native.h"
#include "meta-launcher.h"
#include "backends/meta-cursor-tracker-private.h"
#include "backends/meta-pointer-constraint.h"
#include <stdlib.h>
@@ -139,6 +141,24 @@ constrain_to_barriers (ClutterInputDevice *device,
new_x, new_y);
}
static void
constrain_to_client_constraint (ClutterInputDevice *device,
guint32 time,
float prev_x,
float prev_y,
float *x,
float *y)
{
MetaBackend *backend = meta_get_backend ();
MetaPointerConstraint *constraint = backend->client_pointer_constraint;
if (!constraint)
return;
meta_pointer_constraint_constrain (constraint, device,
time, prev_x, prev_y, x, y);
}
/*
* The pointer constrain code is mostly a rip-off of the XRandR code from Xorg.
* (from xserver/randr/rrcrtc.c, RRConstrainCursorHarder)
@@ -193,10 +213,12 @@ constrain_all_screen_monitors (ClutterInputDevice *device,
static void
pointer_constrain_callback (ClutterInputDevice *device,
guint32 time,
float *new_x,
float *new_y,
gpointer user_data)
guint32 time,
float prev_x,
float prev_y,
float *new_x,
float *new_y,
gpointer user_data)
{
MetaMonitorManager *monitor_manager;
MetaMonitorInfo *monitors;
@@ -205,6 +227,9 @@ pointer_constrain_callback (ClutterInputDevice *device,
/* Constrain to barriers */
constrain_to_barriers (device, time, new_x, new_y);
/* Constrain to pointer lock */
constrain_to_client_constraint (device, time, prev_x, prev_y, new_x, new_y);
monitor_manager = meta_monitor_manager_get ();
monitors = meta_monitor_manager_get_monitor_infos (monitor_manager, &n_monitors);
@@ -255,11 +280,16 @@ meta_backend_native_warp_pointer (MetaBackend *backend,
{
ClutterDeviceManager *manager = clutter_device_manager_get_default ();
ClutterInputDevice *device = clutter_device_manager_get_core_device (manager, CLUTTER_POINTER_DEVICE);
MetaCursorTracker *tracker = meta_cursor_tracker_get_for_screen (NULL);
/* XXX */
guint32 time_ = 0;
/* Warp the input device pointer state. */
clutter_evdev_warp_pointer (device, time_, x, y);
/* Warp displayed pointer cursor. */
meta_cursor_tracker_update_position (tracker, x, y);
}
static void
@@ -306,6 +336,19 @@ meta_backend_native_lock_layout_group (MetaBackend *backend,
g_signal_emit_by_name (backend, "keymap-layout-group-changed", idx, 0);
}
static gboolean
meta_backend_native_get_relative_motion_deltas (MetaBackend *backend,
const ClutterEvent *event,
double *dx,
double *dy,
double *dx_unaccel,
double *dy_unaccel)
{
return clutter_evdev_event_get_relative_motion (event,
dx, dy,
dx_unaccel, dy_unaccel);
}
static void
meta_backend_native_class_init (MetaBackendNativeClass *klass)
{
@@ -323,6 +366,7 @@ meta_backend_native_class_init (MetaBackendNativeClass *klass)
backend_class->set_keymap = meta_backend_native_set_keymap;
backend_class->get_keymap = meta_backend_native_get_keymap;
backend_class->lock_layout_group = meta_backend_native_lock_layout_group;
backend_class->get_relative_motion_deltas = meta_backend_native_get_relative_motion_deltas;
}
static void

View File

@@ -93,126 +93,18 @@ next_serial (void)
return barrier_serial;
}
typedef struct _Vector2
{
float x, y;
} Vector2;
static float
vector2_cross_product (Vector2 a, Vector2 b)
{
return a.x * b.y - a.y * b.x;
}
static Vector2
vector2_add (Vector2 a, Vector2 b)
{
return (Vector2) {
.x = a.x + b.x,
.y = a.y + b.y,
};
}
static Vector2
vector2_subtract (Vector2 a, Vector2 b)
{
return (Vector2) {
.x = a.x - b.x,
.y = a.y - b.y,
};
}
static Vector2
vector2_multiply_constant (float c, Vector2 a)
{
return (Vector2) {
.x = c * a.x,
.y = c * a.y,
};
}
typedef struct _Line2
{
Vector2 a;
Vector2 b;
} Line2;
static gboolean
lines_intersect (Line2 *line1, Line2 *line2, Vector2 *intersection)
{
Vector2 p = line1->a;
Vector2 r = vector2_subtract (line1->b, line1->a);
Vector2 q = line2->a;
Vector2 s = vector2_subtract (line2->b, line2->a);
float rxs;
float sxr;
float t;
float u;
/*
* The line (p, r) and (q, s) intersects where
*
* p + t r = q + u s
*
* Calculate t:
*
* (p + t r) × s = (q + u s) × s
* p × s + t (r × s) = q × s + u (s × s)
* p × s + t (r × s) = q × s
* t (r × s) = q × s - p × s
* t (r × s) = (q - p) × s
* t = ((q - p) × s) / (r × s)
*
* Using the same method, for u we get:
*
* u = ((p - q) × r) / (s × r)
*/
rxs = vector2_cross_product (r, s);
sxr = vector2_cross_product (s, r);
/* If r × s = 0 then the lines are either parallel or collinear. */
if (fabs ( rxs) < DBL_MIN)
return FALSE;
t = vector2_cross_product (vector2_subtract (q, p), s) / rxs;
u = vector2_cross_product (vector2_subtract (p, q), r) / sxr;
/* The lines only intersect if 0 ≤ t ≤ 1 and 0 ≤ u ≤ 1. */
if (t < 0.0 || t > 1.0 || u < 0.0 || u > 1.0)
return FALSE;
*intersection = vector2_add (p, vector2_multiply_constant (t, r));
return TRUE;
}
static gboolean
is_barrier_horizontal (MetaBarrier *barrier)
{
return barrier->priv->y1 == barrier->priv->y2;
return meta_border_is_horizontal (&barrier->priv->border);
}
static gboolean
is_barrier_blocking_directions (MetaBarrier *barrier,
MetaBarrierDirection directions)
{
/* Barriers doesn't block parallel motions. */
if (is_barrier_horizontal (barrier))
{
if ((directions & (META_BARRIER_DIRECTION_POSITIVE_Y |
META_BARRIER_DIRECTION_NEGATIVE_Y)) == 0)
return FALSE;
}
else
{
if ((directions & (META_BARRIER_DIRECTION_POSITIVE_X |
META_BARRIER_DIRECTION_NEGATIVE_X)) == 0)
return FALSE;
}
return (barrier->priv->directions & directions) != directions;
return meta_border_is_blocking_directions (&barrier->priv->border,
directions);
}
static void
@@ -224,31 +116,16 @@ dismiss_pointer (MetaBarrierImplNative *self)
priv->state = META_BARRIER_STATE_LEFT;
}
static Line2
barrier_to_line (MetaBarrier *barrier)
{
return (Line2) {
.a = (Vector2) {
.x = MIN (barrier->priv->x1, barrier->priv->x2),
.y = MIN (barrier->priv->y1, barrier->priv->y2),
},
.b = (Vector2) {
.x = MAX (barrier->priv->x1, barrier->priv->x2),
.y = MAX (barrier->priv->y1, barrier->priv->y2),
},
};
}
/*
* Calculate the hit box for a held motion. The hit box is a 2 px wide region
* in the opposite direction of every direction the barrier blocks. The purpose
* of this is to allow small movements without receiving a "left" signal. This
* heuristic comes from the X.org pointer barrier implementation.
*/
static Line2
static MetaLine2
calculate_barrier_hit_box (MetaBarrier *barrier)
{
Line2 hit_box = barrier_to_line (barrier);
MetaLine2 hit_box = barrier->priv->border.line;
if (is_barrier_horizontal (barrier))
{
@@ -273,7 +150,8 @@ calculate_barrier_hit_box (MetaBarrier *barrier)
}
static gboolean
is_within_box (Line2 box, Vector2 point)
is_within_box (MetaLine2 box,
MetaVector2 point)
{
return (point.x >= box.a.x && point.x < box.b.x &&
point.y >= box.a.y && point.y < box.b.y);
@@ -288,8 +166,8 @@ maybe_release_barrier (gpointer key,
MetaBarrierImplNativePrivate *priv =
meta_barrier_impl_native_get_instance_private (self);
MetaBarrier *barrier = priv->barrier;
Line2 *motion = user_data;
Line2 hit_box;
MetaLine2 *motion = user_data;
MetaLine2 hit_box;
if (priv->state != META_BARRIER_STATE_HELD)
return;
@@ -297,8 +175,10 @@ maybe_release_barrier (gpointer key,
/* Release if we end up outside barrier end points. */
if (is_barrier_horizontal (barrier))
{
if (motion->b.x > MAX (barrier->priv->x1, barrier->priv->x2) ||
motion->b.x < MIN (barrier->priv->x1, barrier->priv->x2))
if (motion->b.x > MAX (barrier->priv->border.line.a.x,
barrier->priv->border.line.b.x) ||
motion->b.x < MIN (barrier->priv->border.line.a.x,
barrier->priv->border.line.b.x))
{
dismiss_pointer (self);
return;
@@ -306,8 +186,10 @@ maybe_release_barrier (gpointer key,
}
else
{
if (motion->b.y > MAX (barrier->priv->y1, barrier->priv->y2) ||
motion->b.y < MIN (barrier->priv->y1, barrier->priv->y2))
if (motion->b.y > MAX (barrier->priv->border.line.a.y,
barrier->priv->border.line.b.y) ||
motion->b.y < MIN (barrier->priv->border.line.a.y,
barrier->priv->border.line.b.y))
{
dismiss_pointer (self);
return;
@@ -330,7 +212,7 @@ maybe_release_barriers (MetaBarrierManagerNative *manager,
float x,
float y)
{
Line2 motion = {
MetaLine2 motion = {
.a = {
.x = prev_x,
.y = prev_y,
@@ -350,7 +232,7 @@ typedef struct _MetaClosestBarrierData
{
struct
{
Line2 motion;
MetaLine2 motion;
MetaBarrierDirection directions;
} in;
@@ -371,8 +253,7 @@ update_closest_barrier (gpointer key,
meta_barrier_impl_native_get_instance_private (self);
MetaBarrier *barrier = priv->barrier;
MetaClosestBarrierData *data = user_data;
Line2 barrier_line;
Vector2 intersection;
MetaVector2 intersection;
float dx, dy;
float distance_2;
@@ -391,17 +272,9 @@ update_closest_barrier (gpointer key,
/* Check if the motion intersects with the barrier, and retrieve the
* intersection point if any. */
barrier_line = (Line2) {
.a = {
.x = barrier->priv->x1,
.y = barrier->priv->y1
},
.b = {
.x = barrier->priv->x2,
.y = barrier->priv->y2
},
};
if (!lines_intersect (&barrier_line, &data->in.motion, &intersection))
if (!meta_line2_intersects_with (&barrier->priv->border.line,
&data->in.motion,
&intersection))
return;
/* Calculate the distance to the barrier and keep track of the closest
@@ -570,9 +443,9 @@ clamp_to_barrier (MetaBarrierImplNative *self,
if (is_barrier_horizontal (barrier))
{
if (*motion_dir & META_BARRIER_DIRECTION_POSITIVE_Y)
*y = barrier->priv->y1;
*y = barrier->priv->border.line.a.y;
else if (*motion_dir & META_BARRIER_DIRECTION_NEGATIVE_Y)
*y = barrier->priv->y1;
*y = barrier->priv->border.line.a.y;
priv->blocked_dir = *motion_dir & (META_BARRIER_DIRECTION_POSITIVE_Y |
META_BARRIER_DIRECTION_NEGATIVE_Y);
@@ -582,9 +455,9 @@ clamp_to_barrier (MetaBarrierImplNative *self,
else
{
if (*motion_dir & META_BARRIER_DIRECTION_POSITIVE_X)
*x = barrier->priv->x1;
*x = barrier->priv->border.line.a.x;
else if (*motion_dir & META_BARRIER_DIRECTION_NEGATIVE_X)
*x = barrier->priv->x1;
*x = barrier->priv->border.line.a.x;
priv->blocked_dir = *motion_dir & (META_BARRIER_DIRECTION_POSITIVE_X |
META_BARRIER_DIRECTION_NEGATIVE_X);

View File

@@ -102,9 +102,6 @@ meta_cursor_renderer_native_finalize (GObject *object)
if (priv->animation_timeout_id)
g_source_remove (priv->animation_timeout_id);
if (priv->gbm)
gbm_device_destroy (priv->gbm);
G_OBJECT_CLASS (meta_cursor_renderer_native_parent_class)->finalize (object);
}
@@ -258,6 +255,9 @@ has_valid_cursor_sprite_gbm_bo (MetaCursorSprite *cursor_sprite)
MetaCursorNativePrivate *cursor_priv =
g_object_get_qdata (G_OBJECT (cursor_sprite), quark_cursor_sprite);
if (!cursor_priv)
return FALSE;
switch (cursor_priv->pending_bo_state)
{
case META_CURSOR_GBM_BO_STATE_NONE:
@@ -273,6 +273,32 @@ has_valid_cursor_sprite_gbm_bo (MetaCursorSprite *cursor_sprite)
return FALSE;
}
static gboolean
cursor_over_transformed_crtc (MetaCursorRenderer *renderer,
MetaCursorSprite *cursor_sprite)
{
MetaMonitorManager *monitors;
MetaCRTC *crtcs;
unsigned int i, n_crtcs;
MetaRectangle rect;
monitors = meta_monitor_manager_get ();
meta_monitor_manager_get_resources (monitors, NULL, NULL,
&crtcs, &n_crtcs, NULL, NULL);
rect = meta_cursor_renderer_calculate_rect (renderer, cursor_sprite);
for (i = 0; i < n_crtcs; i++)
{
if (!meta_rectangle_overlap (&rect, &crtcs[i].rect))
continue;
if (crtcs[i].transform != META_MONITOR_TRANSFORM_NORMAL)
return TRUE;
}
return FALSE;
}
static gboolean
should_have_hw_cursor (MetaCursorRenderer *renderer,
MetaCursorSprite *cursor_sprite)
@@ -282,6 +308,9 @@ should_have_hw_cursor (MetaCursorRenderer *renderer,
if (!cursor_sprite)
return FALSE;
if (cursor_over_transformed_crtc (renderer, cursor_sprite))
return FALSE;
texture = meta_cursor_sprite_get_cogl_texture (cursor_sprite);
if (!texture)
return FALSE;
@@ -640,7 +669,7 @@ meta_cursor_renderer_native_init (MetaCursorRendererNative *native)
{
CoglRenderer *cogl_renderer = cogl_display_get_renderer (cogl_context_get_display (ctx));
priv->drm_fd = cogl_kms_renderer_get_kms_fd (cogl_renderer);
priv->gbm = gbm_create_device (priv->drm_fd);
priv->gbm = cogl_kms_renderer_get_gbm (cogl_renderer);
uint64_t width, height;
if (drmGetCap (priv->drm_fd, DRM_CAP_CURSOR_WIDTH, &width) == 0 &&

View File

@@ -164,56 +164,36 @@ meta_idle_monitor_native_init (MetaIdleMonitorNative *monitor_native)
monitor->watches = g_hash_table_new_full (NULL, NULL, NULL, free_watch);
}
typedef struct {
MetaIdleMonitorNative *monitor_native;
GList *fired_watches;
} CheckNativeClosure;
static gboolean
check_native_watch (gpointer key,
gpointer value,
gpointer user_data)
{
MetaIdleMonitorWatchNative *watch_native = value;
MetaIdleMonitorWatch *watch = (MetaIdleMonitorWatch *) watch_native;
CheckNativeClosure *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_native->timeout_source,
closure->monitor_native->last_event_time +
watch->timeout_msec * 1000);
steal = FALSE;
}
return steal;
}
static void
fire_native_watch (gpointer watch,
gpointer data)
{
_meta_idle_monitor_watch_fire (watch);
}
void
meta_idle_monitor_native_reset_idletime (MetaIdleMonitor *monitor)
{
MetaIdleMonitorNative *monitor_native = META_IDLE_MONITOR_NATIVE (monitor);
CheckNativeClosure closure;
GList *node, *watch_ids;
monitor_native->last_event_time = g_get_monotonic_time ();
closure.monitor_native = monitor_native;
closure.fired_watches = NULL;
g_hash_table_foreach_steal (monitor->watches, check_native_watch, &closure);
watch_ids = g_hash_table_get_keys (monitor->watches);
g_list_foreach (closure.fired_watches, fire_native_watch, NULL);
g_list_free (closure.fired_watches);
for (node = watch_ids; node != NULL; node = node->next)
{
guint watch_id = GPOINTER_TO_UINT (node->data);
MetaIdleMonitorWatchNative *watch;
watch = g_hash_table_lookup (monitor->watches, GUINT_TO_POINTER (watch_id));
if (!watch)
continue;
if (watch->base.timeout_msec == 0)
{
_meta_idle_monitor_watch_fire ((MetaIdleMonitorWatch *) watch);
}
else
{
g_source_set_ready_time (watch->timeout_source,
monitor_native->last_event_time +
watch->base.timeout_msec * 1000);
}
}
g_list_free (watch_ids);
}

View File

@@ -46,6 +46,12 @@
#include "meta-cursor-renderer-native.h"
#include "meta-idle-monitor-native.h"
G_DEFINE_AUTOPTR_CLEANUP_FUNC(GUdevDevice, g_object_unref)
G_DEFINE_AUTOPTR_CLEANUP_FUNC(GUdevClient, g_object_unref)
G_DEFINE_AUTOPTR_CLEANUP_FUNC(GUdevEnumerator, g_object_unref)
G_DEFINE_AUTOPTR_CLEANUP_FUNC(Login1Session, g_object_unref)
G_DEFINE_AUTOPTR_CLEANUP_FUNC(Login1Seat, g_object_unref)
struct _MetaLauncher
{
Login1Session *session_proxy;
@@ -58,8 +64,8 @@ static Login1Session *
get_session_proxy (GCancellable *cancellable,
GError **error)
{
char *proxy_path;
char *session_id;
g_autofree char *proxy_path = NULL;
g_autofree char *session_id = NULL;
Login1Session *session_proxy;
if (sd_pid_get_session (getpid (), &session_id) < 0)
@@ -81,8 +87,6 @@ get_session_proxy (GCancellable *cancellable,
if (!session_proxy)
g_prefix_error(error, "Could not get session proxy: ");
free (proxy_path);
return session_proxy;
}
@@ -285,8 +289,8 @@ get_primary_gpu_path (const gchar *seat_name)
gchar *path = NULL;
GList *devices, *tmp;
GUdevClient *gudev_client = g_udev_client_new (subsystems);
GUdevEnumerator *enumerator = g_udev_enumerator_new (gudev_client);
g_autoptr (GUdevClient) gudev_client = g_udev_client_new (subsystems);
g_autoptr (GUdevEnumerator) enumerator = g_udev_enumerator_new (gudev_client);
g_udev_enumerator_add_match_name (enumerator, "card*");
g_udev_enumerator_add_match_tag (enumerator, "seat");
@@ -295,9 +299,10 @@ get_primary_gpu_path (const gchar *seat_name)
if (!devices)
goto out;
for (tmp = devices; tmp != NULL && path == NULL; tmp = tmp->next)
for (tmp = devices; tmp != NULL; tmp = tmp->next)
{
GUdevDevice *platform_device = NULL, *pci_device = NULL;
g_autoptr (GUdevDevice) platform_device = NULL;
g_autoptr (GUdevDevice) pci_device = NULL;
GUdevDevice *dev = tmp->data;
gint boot_vga;
const gchar *device_seat;
@@ -325,13 +330,14 @@ get_primary_gpu_path (const gchar *seat_name)
continue;
platform_device = g_udev_device_get_parent_with_subsystem (dev, "platform", NULL);
pci_device = g_udev_device_get_parent_with_subsystem (dev, "pci", NULL);
if (platform_device != NULL)
{
path = g_strdup (g_udev_device_get_device_file (dev));
break;
}
else if (pci_device != NULL)
pci_device = g_udev_device_get_parent_with_subsystem (dev, "pci", NULL);
if (pci_device != NULL)
{
/* get value of boot_vga attribute or 0 if the device has no boot_vga */
boot_vga = g_udev_device_get_sysfs_attr_as_int (pci_device, "boot_vga");
@@ -339,19 +345,14 @@ get_primary_gpu_path (const gchar *seat_name)
{
/* found the boot_vga device */
path = g_strdup (g_udev_device_get_device_file (dev));
break;
}
}
g_object_unref (platform_device);
g_object_unref (pci_device);
}
g_list_free_full (devices, g_object_unref);
out:
g_object_unref (enumerator);
g_object_unref (gudev_client);
return path;
}
@@ -397,7 +398,8 @@ get_kms_fd (Login1Session *session_proxy,
static gchar *
get_seat_id (GError **error)
{
char *session_id, *seat_id;
g_autofree char *session_id = NULL;
char *seat_id = NULL;
int r;
r = sd_pid_get_session (0, &session_id);
@@ -411,8 +413,6 @@ get_seat_id (GError **error)
}
r = sd_session_get_seat (session_id, &seat_id);
free (session_id);
if (r < 0)
{
g_set_error (error,
@@ -429,9 +429,9 @@ MetaLauncher *
meta_launcher_new (GError **error)
{
MetaLauncher *self = NULL;
Login1Session *session_proxy = NULL;
Login1Seat *seat_proxy = NULL;
char *seat_id = NULL;
g_autoptr (Login1Session) session_proxy = NULL;
g_autoptr (Login1Seat) seat_proxy = NULL;
g_autofree char *seat_id = NULL;
gboolean have_control = FALSE;
int kms_fd;
@@ -458,11 +458,9 @@ meta_launcher_new (GError **error)
if (!get_kms_fd (session_proxy, seat_id, &kms_fd, error))
goto fail;
free (seat_id);
self = g_slice_new0 (MetaLauncher);
self->session_proxy = session_proxy;
self->seat_proxy = seat_proxy;
self->session_proxy = g_object_ref (session_proxy);
self->seat_proxy = g_object_ref (seat_proxy);
self->session_active = TRUE;
@@ -477,10 +475,6 @@ meta_launcher_new (GError **error)
fail:
if (have_control)
login1_session_call_release_control_sync (session_proxy, NULL, NULL);
g_clear_object (&session_proxy);
g_clear_object (&seat_proxy);
free (seat_id);
return NULL;
}

View File

@@ -42,6 +42,8 @@
#include <gudev/gudev.h>
#define ALL_TRANSFORMS (META_MONITOR_TRANSFORM_FLIPPED_270 + 1)
typedef struct {
drmModeConnector *connector;
@@ -66,6 +68,9 @@ typedef struct {
uint32_t underscan_prop_id;
uint32_t underscan_hborder_prop_id;
uint32_t underscan_vborder_prop_id;
uint32_t primary_plane_id;
uint32_t rotation_prop_id;
uint32_t rotation_map[ALL_TRANSFORMS];
} MetaCRTCKms;
struct _MetaMonitorManagerKms
@@ -429,6 +434,137 @@ get_output_scale (MetaMonitorManager *manager,
return compute_scale (output);
}
static int
find_property_index (MetaMonitorManager *manager,
drmModeObjectPropertiesPtr props,
const gchar *prop_name,
drmModePropertyPtr *found)
{
MetaMonitorManagerKms *manager_kms = META_MONITOR_MANAGER_KMS (manager);
unsigned int i;
for (i = 0; i < props->count_props; i++)
{
drmModePropertyPtr prop;
prop = drmModeGetProperty (manager_kms->fd, props->props[i]);
if (!prop)
continue;
if (strcmp (prop->name, prop_name) == 0)
{
*found = prop;
return i;
}
drmModeFreeProperty (prop);
}
return -1;
}
static void
parse_transforms (MetaMonitorManager *manager,
drmModePropertyPtr prop,
MetaCRTC *crtc)
{
MetaCRTCKms *crtc_kms = crtc->driver_private;
int i;
for (i = 0; i < prop->count_enums; i++)
{
int cur = -1;
if (strcmp (prop->enums[i].name, "rotate-0") == 0)
cur = META_MONITOR_TRANSFORM_NORMAL;
else if (strcmp (prop->enums[i].name, "rotate-90") == 0)
cur = META_MONITOR_TRANSFORM_90;
else if (strcmp (prop->enums[i].name, "rotate-180") == 0)
cur = META_MONITOR_TRANSFORM_180;
else if (strcmp (prop->enums[i].name, "rotate-270") == 0)
cur = META_MONITOR_TRANSFORM_270;
if (cur != -1)
{
crtc->all_transforms |= 1 << cur;
crtc_kms->rotation_map[cur] = 1 << prop->enums[i].value;
}
}
}
static gboolean
is_primary_plane (MetaMonitorManager *manager,
drmModeObjectPropertiesPtr props)
{
drmModePropertyPtr prop;
int idx;
idx = find_property_index (manager, props, "type", &prop);
if (idx < 0)
return FALSE;
drmModeFreeProperty (prop);
return props->prop_values[idx] == DRM_PLANE_TYPE_PRIMARY;
}
static void
init_crtc_rotations (MetaMonitorManager *manager,
MetaCRTC *crtc,
unsigned int idx)
{
MetaMonitorManagerKms *manager_kms = META_MONITOR_MANAGER_KMS (manager);
drmModeObjectPropertiesPtr props;
drmModePlaneRes *planes;
drmModePlane *drm_plane;
MetaCRTCKms *crtc_kms;
unsigned int i;
crtc_kms = crtc->driver_private;
planes = drmModeGetPlaneResources(manager_kms->fd);
if (planes == NULL)
return;
for (i = 0; i < planes->count_planes; i++)
{
drmModePropertyPtr prop;
drm_plane = drmModeGetPlane (manager_kms->fd, planes->planes[i]);
if (!drm_plane)
continue;
if ((drm_plane->possible_crtcs & (1 << idx)))
{
props = drmModeObjectGetProperties (manager_kms->fd,
drm_plane->plane_id,
DRM_MODE_OBJECT_PLANE);
if (props && is_primary_plane (manager, props))
{
int rotation_idx;
crtc_kms->primary_plane_id = drm_plane->plane_id;
rotation_idx = find_property_index (manager, props, "rotation", &prop);
if (rotation_idx >= 0)
{
crtc_kms->rotation_prop_id = props->props[rotation_idx];
parse_transforms (manager, prop, crtc);
drmModeFreeProperty (prop);
}
}
if (props)
drmModeFreeObjectProperties (props);
}
drmModeFreePlane (drm_plane);
}
drmModeFreePlaneResources (planes);
}
static void
meta_monitor_manager_kms_read_current (MetaMonitorManager *manager)
{
@@ -556,6 +692,7 @@ meta_monitor_manager_kms_read_current (MetaMonitorManager *manager)
meta_crtc->driver_private = g_new0 (MetaCRTCKms, 1);
meta_crtc->driver_notify = (GDestroyNotify) meta_crtc_destroy_notify;
find_crtc_properties (manager_kms, meta_crtc);
init_crtc_rotations (manager, meta_crtc, i);
drmModeFreeCrtc (crtc);
}
@@ -938,6 +1075,7 @@ meta_monitor_manager_kms_apply_configuration (MetaMonitorManager *manager,
{
MetaCRTCInfo *crtc_info = crtcs[i];
MetaCRTC *crtc = crtc_info->crtc;
MetaCRTCKms *crtc_kms = crtc->driver_private;
CoglKmsCrtc *cogl_crtc;
crtc->is_dirty = TRUE;
@@ -1010,6 +1148,13 @@ meta_monitor_manager_kms_apply_configuration (MetaMonitorManager *manager,
crtc->current_mode = mode;
crtc->transform = crtc_info->transform;
}
if (crtc->all_transforms & (1 << crtc->transform))
drmModeObjectSetProperty (manager_kms->fd,
crtc_kms->primary_plane_id,
DRM_MODE_OBJECT_PLANE,
crtc_kms->rotation_prop_id,
crtc_kms->rotation_map[crtc->transform]);
}
/* Disable CRTCs not mentioned in the list (they have is_dirty == FALSE,
@@ -1162,6 +1307,8 @@ meta_monitor_manager_kms_init (MetaMonitorManagerKms *manager_kms)
manager_kms->fd = cogl_kms_renderer_get_kms_fd (cogl_renderer);
drmSetClientCap (manager_kms->fd, DRM_CLIENT_CAP_UNIVERSAL_PLANES, 1);
const char *subsystems[2] = { "drm", NULL };
manager_kms->udev = g_udev_client_new (subsystems);
g_signal_connect (manager_kms->udev, "uevent",

View File

@@ -107,6 +107,7 @@ meta_barrier_impl_x11_new (MetaBarrier *barrier)
MetaDisplay *display = barrier->priv->display;
Display *dpy;
Window root;
unsigned int allowed_motion_dirs;
if (display == NULL)
{
@@ -121,12 +122,14 @@ meta_barrier_impl_x11_new (MetaBarrier *barrier)
dpy = display->xdisplay;
root = DefaultRootWindow (dpy);
allowed_motion_dirs =
meta_border_get_allows_directions (&barrier->priv->border);
priv->xbarrier = XFixesCreatePointerBarrier (dpy, root,
barrier->priv->x1,
barrier->priv->y1,
barrier->priv->x2,
barrier->priv->y2,
barrier->priv->directions,
barrier->priv->border.line.a.x,
barrier->priv->border.line.a.y,
barrier->priv->border.line.b.x,
barrier->priv->border.line.b.y,
allowed_motion_dirs,
0, NULL);
g_hash_table_insert (display->xids, &priv->xbarrier, barrier);

View File

@@ -107,20 +107,6 @@ set_alarm_enabled (Display *dpy,
XSyncChangeAlarm (dpy, alarm, XSyncCAEvents, &attr);
}
static void
check_x11_watch (gpointer data,
gpointer user_data)
{
MetaIdleMonitorWatchXSync *watch_xsync = data;
MetaIdleMonitorWatch *watch = (MetaIdleMonitorWatch *) watch_xsync;
XSyncAlarm alarm = (XSyncAlarm) user_data;
if (watch_xsync->xalarm != alarm)
return;
_meta_idle_monitor_watch_fire (watch);
}
static char *
counter_name_for_device (int device_id)
{
@@ -327,13 +313,38 @@ meta_idle_monitor_xsync_init (MetaIdleMonitorXSync *monitor_xsync)
monitor_xsync->alarms = g_hash_table_new (NULL, NULL);
}
static void
check_x11_watches (MetaIdleMonitor *monitor,
XSyncAlarm alarm)
{
GList *node, *watch_ids;
/* we get the keys and do explicit look ups in case
* an early iteration of the loop ends up leading
* to watches from later iterations getting invalidated
*/
watch_ids = g_hash_table_get_keys (monitor->watches);
for (node = watch_ids; node != NULL; node = node->next)
{
guint watch_id = GPOINTER_TO_UINT (node->data);
MetaIdleMonitorWatchXSync *watch;
watch = g_hash_table_lookup (monitor->watches, GUINT_TO_POINTER (watch_id));
if (watch && watch->xalarm == alarm)
_meta_idle_monitor_watch_fire ((MetaIdleMonitorWatch *) watch);
}
g_list_free (watch_ids);
}
void
meta_idle_monitor_xsync_handle_xevent (MetaIdleMonitor *monitor,
XSyncAlarmNotifyEvent *alarm_event)
{
MetaIdleMonitorXSync *monitor_xsync = META_IDLE_MONITOR_XSYNC (monitor);
XSyncAlarm alarm;
GList *watches;
gboolean has_alarm;
if (alarm_event->state != XSyncAlarmActive)
@@ -358,10 +369,5 @@ meta_idle_monitor_xsync_handle_xevent (MetaIdleMonitor *monitor,
}
if (has_alarm)
{
watches = g_hash_table_get_values (monitor->watches);
g_list_foreach (watches, check_x11_watch, (gpointer) alarm);
g_list_free (watches);
}
check_x11_watches (monitor, alarm);
}

View File

@@ -419,12 +419,6 @@ read_output_edid (MetaMonitorManagerXrandr *manager_xrandr,
result = get_edid_property (manager_xrandr->xdisplay, winsys_id, edid_atom, &len);
}
if (!result)
{
edid_atom = XInternAtom (manager_xrandr->xdisplay, "XFree86_DDC_EDID1_RAWDATA", FALSE);
result = get_edid_property (manager_xrandr->xdisplay, winsys_id, edid_atom, &len);
}
if (result)
{
if (len > 0 && len % 128 == 0)

View File

@@ -36,6 +36,14 @@
#include "compositor/region-utils.h"
enum {
PAINTING,
LAST_SIGNAL
};
static guint signals[LAST_SIGNAL];
struct _MetaSurfaceActorWaylandPrivate
{
MetaWaylandSurface *surface;
@@ -347,12 +355,13 @@ meta_surface_actor_wayland_paint (ClutterActor *actor)
if (priv->surface)
{
MetaWaylandCompositor *compositor = priv->surface->compositor;
meta_wayland_surface_update_outputs (priv->surface);
wl_list_insert_list (&compositor->frame_callbacks, &priv->frame_callback_list);
wl_list_init (&priv->frame_callback_list);
}
g_signal_emit (actor, signals[PAINTING], 0);
CLUTTER_ACTOR_CLASS (meta_surface_actor_wayland_parent_class)->paint (actor);
}
@@ -388,6 +397,13 @@ meta_surface_actor_wayland_class_init (MetaSurfaceActorWaylandClass *klass)
surface_actor_class->get_window = meta_surface_actor_wayland_get_window;
object_class->dispose = meta_surface_actor_wayland_dispose;
signals[PAINTING] = g_signal_new ("painting",
G_TYPE_FROM_CLASS (object_class),
G_SIGNAL_RUN_LAST,
0,
NULL, NULL, NULL,
G_TYPE_NONE, 0);
}
static void

View File

@@ -35,6 +35,7 @@
#include <meta/boxes.h>
#include <meta/display.h>
#include "keybindings-private.h"
#include "startup-notification-private.h"
#include "meta-gesture-tracker-private.h"
#include <meta/prefs.h>
#include <meta/barrier.h>
@@ -276,9 +277,8 @@ struct _MetaDisplay
int xinput_event_base;
int xinput_opcode;
#ifdef HAVE_STARTUP_NOTIFICATION
SnDisplay *sn_display;
#endif
MetaStartupNotification *startup_notification;
int xsync_event_base;
int xsync_error_base;
int shape_event_base;

View File

@@ -400,28 +400,6 @@ meta_display_remove_pending_pings_for_window (MetaDisplay *display,
}
#ifdef HAVE_STARTUP_NOTIFICATION
static void
sn_error_trap_push (SnDisplay *sn_display,
Display *xdisplay)
{
MetaDisplay *display;
display = meta_display_for_x_display (xdisplay);
if (display != NULL)
meta_error_trap_push (display);
}
static void
sn_error_trap_pop (SnDisplay *sn_display,
Display *xdisplay)
{
MetaDisplay *display;
display = meta_display_for_x_display (xdisplay);
if (display != NULL)
meta_error_trap_pop (display);
}
#endif
static void
enable_compositor (MetaDisplay *display)
{
@@ -527,6 +505,20 @@ gesture_tracker_state_changed (MetaGestureTracker *tracker,
}
}
static void
on_startup_notification_changed (MetaStartupNotification *sn,
gpointer sequence,
MetaDisplay *display)
{
if (!display->screen)
return;
g_slist_free (display->screen->startup_sequences);
display->screen->startup_sequences =
meta_startup_notification_get_sequences (display->startup_notification);
g_signal_emit_by_name (display->screen, "startup-sequence-changed", sequence);
}
/**
* meta_display_open:
*
@@ -630,12 +622,6 @@ meta_display_open (void)
display->screen = NULL;
#ifdef HAVE_STARTUP_NOTIFICATION
display->sn_display = sn_display_new (display->xdisplay,
sn_error_trap_push,
sn_error_trap_pop);
#endif
/* Get events */
meta_display_init_events (display);
meta_display_init_events_x11 (display);
@@ -916,6 +902,10 @@ meta_display_open (void)
display->screen = screen;
display->startup_notification = meta_startup_notification_get (display);
g_signal_connect (display->startup_notification, "changed",
G_CALLBACK (on_startup_notification_changed), display);
meta_screen_init_workspaces (screen);
enable_compositor (display);
@@ -1100,6 +1090,7 @@ meta_display_close (MetaDisplay *display,
meta_display_remove_autoraise_callback (display);
g_clear_object (&display->startup_notification);
g_clear_object (&display->gesture_tracker);
if (display->focus_timeout_id)
@@ -1112,14 +1103,6 @@ meta_display_close (MetaDisplay *display,
meta_screen_free (display->screen, timestamp);
#ifdef HAVE_STARTUP_NOTIFICATION
if (display->sn_display)
{
sn_display_unref (display->sn_display);
display->sn_display = NULL;
}
#endif
/* Must be after all calls to meta_window_unmanage() since they
* unregister windows
*/

154
src/core/meta-border.c Normal file
View File

@@ -0,0 +1,154 @@
/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
/*
* Copyright (C) 2015 Red Hat
*
* 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.
*
* Written by:
* Jonas Ådahl <jadahl@gmail.com>
*/
#include "config.h"
#include "core/meta-border.h"
#include <math.h>
static inline float
meta_vector2_cross_product (const MetaVector2 a,
const MetaVector2 b)
{
return a.x * b.y - a.y * b.x;
}
static inline MetaVector2
meta_vector2_add (const MetaVector2 a,
const MetaVector2 b)
{
return (MetaVector2) {
.x = a.x + b.x,
.y = a.y + b.y,
};
}
static inline MetaVector2
meta_vector2_multiply_constant (const float c,
const MetaVector2 a)
{
return (MetaVector2) {
.x = c * a.x,
.y = c * a.y,
};
}
gboolean
meta_line2_intersects_with (const MetaLine2 *line1,
const MetaLine2 *line2,
MetaVector2 *intersection)
{
MetaVector2 p = line1->a;
MetaVector2 r = meta_vector2_subtract (line1->b, line1->a);
MetaVector2 q = line2->a;
MetaVector2 s = meta_vector2_subtract (line2->b, line2->a);
float rxs;
float sxr;
float t;
float u;
/*
* The line (p, r) and (q, s) intersects where
*
* p + t r = q + u s
*
* Calculate t:
*
* (p + t r) × s = (q + u s) × s
* p × s + t (r × s) = q × s + u (s × s)
* p × s + t (r × s) = q × s
* t (r × s) = q × s - p × s
* t (r × s) = (q - p) × s
* t = ((q - p) × s) / (r × s)
*
* Using the same method, for u we get:
*
* u = ((p - q) × r) / (s × r)
*/
rxs = meta_vector2_cross_product (r, s);
sxr = meta_vector2_cross_product (s, r);
/* If r × s = 0 then the lines are either parallel or collinear. */
if (fabsf (rxs) < FLT_MIN)
return FALSE;
t = meta_vector2_cross_product (meta_vector2_subtract (q, p), s) / rxs;
u = meta_vector2_cross_product (meta_vector2_subtract (p, q), r) / sxr;
/* The lines only intersect if 0 ≤ t ≤ 1 and 0 ≤ u ≤ 1. */
if (t < 0.0 || t > 1.0 || u < 0.0 || u > 1.0)
return FALSE;
*intersection = meta_vector2_add (p, meta_vector2_multiply_constant (t, r));
return TRUE;
}
gboolean
meta_border_is_horizontal (MetaBorder *border)
{
return border->line.a.y == border->line.b.y;
}
gboolean
meta_border_is_blocking_directions (MetaBorder *border,
MetaBorderMotionDirection directions)
{
if (meta_border_is_horizontal (border))
{
if ((directions & (META_BORDER_MOTION_DIRECTION_POSITIVE_Y |
META_BORDER_MOTION_DIRECTION_NEGATIVE_Y)) == 0)
return FALSE;
}
else
{
if ((directions & (META_BORDER_MOTION_DIRECTION_POSITIVE_X |
META_BORDER_MOTION_DIRECTION_NEGATIVE_X)) == 0)
return FALSE;
}
return (~border->blocking_directions & directions) != directions;
}
unsigned int
meta_border_get_allows_directions (MetaBorder *border)
{
return ~border->blocking_directions &
(META_BORDER_MOTION_DIRECTION_POSITIVE_X |
META_BORDER_MOTION_DIRECTION_POSITIVE_Y |
META_BORDER_MOTION_DIRECTION_NEGATIVE_X |
META_BORDER_MOTION_DIRECTION_NEGATIVE_Y);
}
void
meta_border_set_allows_directions (MetaBorder *border, unsigned int directions)
{
border->blocking_directions =
~directions & (META_BORDER_MOTION_DIRECTION_POSITIVE_X |
META_BORDER_MOTION_DIRECTION_POSITIVE_Y |
META_BORDER_MOTION_DIRECTION_NEGATIVE_X |
META_BORDER_MOTION_DIRECTION_NEGATIVE_Y);
}

84
src/core/meta-border.h Normal file
View File

@@ -0,0 +1,84 @@
/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
/*
* Copyright (C) 2015 Red Hat
*
* 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.
*
* Written by:
* Jonas Ådahl <jadahl@gmail.com>
*/
#ifndef META_BORDER_H
#define META_BORDER_H
#include <glib.h>
typedef enum
{
META_BORDER_MOTION_DIRECTION_POSITIVE_X = 1 << 0,
META_BORDER_MOTION_DIRECTION_POSITIVE_Y = 1 << 1,
META_BORDER_MOTION_DIRECTION_NEGATIVE_X = 1 << 2,
META_BORDER_MOTION_DIRECTION_NEGATIVE_Y = 1 << 3,
} MetaBorderMotionDirection;
typedef struct _MetaVector2
{
float x;
float y;
} MetaVector2;
typedef struct _MetaLine2
{
MetaVector2 a;
MetaVector2 b;
} MetaLine2;
typedef struct _MetaBorder
{
MetaLine2 line;
MetaBorderMotionDirection blocking_directions;
} MetaBorder;
static inline MetaVector2
meta_vector2_subtract (const MetaVector2 a,
const MetaVector2 b)
{
return (MetaVector2) {
.x = a.x - b.x,
.y = a.y - b.y,
};
}
gboolean
meta_line2_intersects_with (const MetaLine2 *line1,
const MetaLine2 *line2,
MetaVector2 *intersection);
gboolean
meta_border_is_horizontal (MetaBorder *border);
gboolean
meta_border_is_blocking_directions (MetaBorder *border,
MetaBorderMotionDirection directions);
unsigned int
meta_border_get_allows_directions (MetaBorder *border);
void
meta_border_set_allows_directions (MetaBorder *border, unsigned int directions);
#endif /* META_BORDER_H */

View File

@@ -85,11 +85,7 @@ struct _MetaScreen
/* Cache the current monitor */
int last_monitor_index;
#ifdef HAVE_STARTUP_NOTIFICATION
SnMonitorContext *sn_context;
GSList *startup_sequences;
guint startup_sequence_timeout;
#endif
Window wm_cm_selection_window;
guint work_area_later;

View File

@@ -71,11 +71,6 @@ static void prefs_changed_callback (MetaPreference pref,
static void set_desktop_geometry_hint (MetaScreen *screen);
static void set_desktop_viewport_hint (MetaScreen *screen);
#ifdef HAVE_STARTUP_NOTIFICATION
static void meta_screen_sn_event (SnMonitorEvent *event,
void *user_data);
#endif
static void on_monitors_changed (MetaMonitorManager *manager,
MetaScreen *screen);
@@ -730,17 +725,6 @@ meta_screen_new (MetaDisplay *display,
meta_prefs_add_listener (prefs_changed_callback, screen);
#ifdef HAVE_STARTUP_NOTIFICATION
screen->sn_context =
sn_monitor_context_new (screen->display->sn_display,
screen->number,
meta_screen_sn_event,
screen,
NULL);
screen->startup_sequences = NULL;
screen->startup_sequence_timeout = 0;
#endif
meta_verbose ("Added screen %d ('%s') root 0x%lx\n",
screen->number, screen->screen_name, screen->xroot);
@@ -800,24 +784,6 @@ meta_screen_free (MetaScreen *screen,
meta_screen_ungrab_keys (screen);
#ifdef HAVE_STARTUP_NOTIFICATION
g_slist_foreach (screen->startup_sequences,
(GFunc) sn_startup_sequence_unref, NULL);
g_slist_free (screen->startup_sequences);
screen->startup_sequences = NULL;
if (screen->startup_sequence_timeout != 0)
{
g_source_remove (screen->startup_sequence_timeout);
screen->startup_sequence_timeout = 0;
}
if (screen->sn_context)
{
sn_monitor_context_unref (screen->sn_context);
screen->sn_context = NULL;
}
#endif
meta_ui_free (screen->ui);
meta_stack_free (screen->stack);
@@ -2538,208 +2504,6 @@ meta_screen_unshow_desktop (MetaScreen *screen)
meta_screen_update_showing_desktop_hint (screen);
}
#ifdef HAVE_STARTUP_NOTIFICATION
static gboolean startup_sequence_timeout (void *data);
static void
update_startup_feedback (MetaScreen *screen)
{
if (screen->startup_sequences != NULL)
{
meta_topic (META_DEBUG_STARTUP,
"Setting busy cursor\n");
meta_screen_set_cursor (screen, META_CURSOR_BUSY);
}
else
{
meta_topic (META_DEBUG_STARTUP,
"Setting default cursor\n");
meta_screen_set_cursor (screen, META_CURSOR_DEFAULT);
}
}
static void
add_sequence (MetaScreen *screen,
SnStartupSequence *sequence)
{
meta_topic (META_DEBUG_STARTUP,
"Adding sequence %s\n",
sn_startup_sequence_get_id (sequence));
sn_startup_sequence_ref (sequence);
screen->startup_sequences = g_slist_prepend (screen->startup_sequences,
sequence);
/* our timeout just polls every second, instead of bothering
* to compute exactly when we may next time out
*/
if (screen->startup_sequence_timeout == 0)
{
screen->startup_sequence_timeout = g_timeout_add_seconds (1,
startup_sequence_timeout,
screen);
g_source_set_name_by_id (screen->startup_sequence_timeout,
"[mutter] startup_sequence_timeout");
}
update_startup_feedback (screen);
}
static void
remove_sequence (MetaScreen *screen,
SnStartupSequence *sequence)
{
meta_topic (META_DEBUG_STARTUP,
"Removing sequence %s\n",
sn_startup_sequence_get_id (sequence));
screen->startup_sequences = g_slist_remove (screen->startup_sequences,
sequence);
if (screen->startup_sequences == NULL &&
screen->startup_sequence_timeout != 0)
{
g_source_remove (screen->startup_sequence_timeout);
screen->startup_sequence_timeout = 0;
}
update_startup_feedback (screen);
sn_startup_sequence_unref (sequence);
}
typedef struct
{
GSList *list;
GTimeVal now;
} CollectTimedOutData;
/* This should be fairly long, as it should never be required unless
* apps or .desktop files are buggy, and it's confusing if
* OpenOffice or whatever seems to stop launching - people
* might decide they need to launch it again.
*/
#define STARTUP_TIMEOUT 15000
static void
collect_timed_out_foreach (void *element,
void *data)
{
CollectTimedOutData *ctod = data;
SnStartupSequence *sequence = element;
long tv_sec, tv_usec;
double elapsed;
sn_startup_sequence_get_last_active_time (sequence, &tv_sec, &tv_usec);
elapsed =
((((double)ctod->now.tv_sec - tv_sec) * G_USEC_PER_SEC +
(ctod->now.tv_usec - tv_usec))) / 1000.0;
meta_topic (META_DEBUG_STARTUP,
"Sequence used %g seconds vs. %g max: %s\n",
elapsed, (double) STARTUP_TIMEOUT,
sn_startup_sequence_get_id (sequence));
if (elapsed > STARTUP_TIMEOUT)
ctod->list = g_slist_prepend (ctod->list, sequence);
}
static gboolean
startup_sequence_timeout (void *data)
{
MetaScreen *screen = data;
CollectTimedOutData ctod;
GSList *l;
ctod.list = NULL;
g_get_current_time (&ctod.now);
g_slist_foreach (screen->startup_sequences,
collect_timed_out_foreach,
&ctod);
for (l = ctod.list; l != NULL; l = l->next)
{
SnStartupSequence *sequence = l->data;
meta_topic (META_DEBUG_STARTUP,
"Timed out sequence %s\n",
sn_startup_sequence_get_id (sequence));
sn_startup_sequence_complete (sequence);
}
g_slist_free (ctod.list);
if (screen->startup_sequences != NULL)
{
return TRUE;
}
else
{
/* remove */
screen->startup_sequence_timeout = 0;
return FALSE;
}
}
static void
meta_screen_sn_event (SnMonitorEvent *event,
void *user_data)
{
MetaScreen *screen;
SnStartupSequence *sequence;
screen = user_data;
sequence = sn_monitor_event_get_startup_sequence (event);
sn_startup_sequence_ref (sequence);
switch (sn_monitor_event_get_type (event))
{
case SN_MONITOR_EVENT_INITIATED:
{
const char *wmclass;
wmclass = sn_startup_sequence_get_wmclass (sequence);
meta_topic (META_DEBUG_STARTUP,
"Received startup initiated for %s wmclass %s\n",
sn_startup_sequence_get_id (sequence),
wmclass ? wmclass : "(unset)");
add_sequence (screen, sequence);
}
break;
case SN_MONITOR_EVENT_COMPLETED:
{
meta_topic (META_DEBUG_STARTUP,
"Received startup completed for %s\n",
sn_startup_sequence_get_id (sequence));
remove_sequence (screen,
sn_monitor_event_get_startup_sequence (event));
}
break;
case SN_MONITOR_EVENT_CHANGED:
meta_topic (META_DEBUG_STARTUP,
"Received startup changed for %s\n",
sn_startup_sequence_get_id (sequence));
break;
case SN_MONITOR_EVENT_CANCELED:
meta_topic (META_DEBUG_STARTUP,
"Received startup canceled for %s\n",
sn_startup_sequence_get_id (sequence));
break;
}
g_signal_emit (G_OBJECT (screen), screen_signals[STARTUP_SEQUENCE_CHANGED], 0, sequence);
sn_startup_sequence_unref (sequence);
}
/**
* meta_screen_get_startup_sequences: (skip)
* @screen:
@@ -2751,7 +2515,6 @@ meta_screen_get_startup_sequences (MetaScreen *screen)
{
return screen->startup_sequences;
}
#endif
/* Sets the initial_timestamp and initial_workspace properties
* of a window according to information given us by the

View File

@@ -0,0 +1,48 @@
/* -*- 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
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License as
* published by the Free Software Foundation; either version 2 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, see <http://www.gnu.org/licenses/>.
*/
#ifndef META_STARTUP_NOTIFICATION_PRIVATE_H
#define META_STARTUP_NOTIFICATION_PRIVATE_H
#include "display-private.h"
#define META_TYPE_STARTUP_NOTIFICATION (meta_startup_notification_get_type ())
G_DECLARE_FINAL_TYPE (MetaStartupNotification,
meta_startup_notification,
META, STARTUP_NOTIFICATION,
GObject)
MetaStartupNotification *
meta_startup_notification_get (MetaDisplay *display);
gboolean meta_startup_notification_handle_xevent (MetaStartupNotification *sn,
XEvent *xevent);
void meta_startup_notification_remove_sequence (MetaStartupNotification *sn,
const gchar *id);
GSList * meta_startup_notification_get_sequences (MetaStartupNotification *sn);
#endif /* META_STARTUP_NOTIFICATION_PRIVATE_H */

View File

@@ -0,0 +1,735 @@
/* -*- 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
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License as
* published by the Free Software Foundation; either version 2 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, see <http://www.gnu.org/licenses/>.
*/
#include <config.h>
#include <glib-object.h>
#include <meta/errors.h>
#include "display-private.h"
#include "screen-private.h"
#include "startup-notification-private.h"
/* This should be fairly long, as it should never be required unless
* apps or .desktop files are buggy, and it's confusing if
* OpenOffice or whatever seems to stop launching - people
* might decide they need to launch it again.
*/
#define STARTUP_TIMEOUT 15000000
typedef struct _MetaStartupNotification MetaStartupNotification;
typedef struct _MetaStartupNotificationSequence MetaStartupNotificationSequence;
typedef struct _MetaStartupNotificationSequenceClass MetaStartupNotificationSequenceClass;
typedef struct _MetaStartupNotificationSequenceX11 MetaStartupNotificationSequenceX11;
typedef struct _MetaStartupNotificationSequenceX11Class MetaStartupNotificationSequenceX11Class;
enum {
PROP_SN_0,
PROP_SN_DISPLAY,
N_SN_PROPS
};
enum {
PROP_SEQ_0,
PROP_SEQ_ID,
PROP_SEQ_TIMESTAMP,
N_SEQ_PROPS
};
enum {
SN_CHANGED,
N_SN_SIGNALS
};
static guint sn_signals[N_SN_SIGNALS];
static GParamSpec *sn_props[N_SN_PROPS];
static GParamSpec *seq_props[N_SEQ_PROPS];
typedef struct
{
GSList *list;
gint64 now;
} CollectTimedOutData;
struct _MetaStartupNotification
{
GObject parent_instance;
MetaDisplay *display;
#ifdef HAVE_STARTUP_NOTIFICATION
SnDisplay *sn_display;
SnMonitorContext *sn_context;
#endif
GSList *startup_sequences;
guint startup_sequence_timeout;
};
struct _MetaStartupNotificationSequence {
GObject parent_instance;
gchar *id;
time_t timestamp;
};
struct _MetaStartupNotificationSequenceClass {
GObjectClass parent_class;
void (* complete) (MetaStartupNotificationSequence *sequence);
};
GType meta_startup_notification_get_type (void) G_GNUC_CONST;
GType meta_startup_notification_sequence_get_type (void) G_GNUC_CONST;
#define META_TYPE_STARTUP_NOTIFICATION_SEQUENCE (meta_startup_notification_sequence_get_type ())
#define META_STARTUP_NOTIFICATION_SEQUENCE(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), META_TYPE_STARTUP_NOTIFICATION_SEQUENCE, MetaStartupNotificationSequence))
#define META_STARTUP_NOTIFICATION_SEQUENCE_CLASS(k) (G_TYPE_CHECK_CLASS_CAST ((k), META_TYPE_STARTUP_NOTIFICATION_SEQUENCE, MetaStartupNotificationSequenceClass))
#define META_IS_STARTUP_NOTIFICATION_SEQUENCE(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), META_TYPE_STARTUP_NOTIFICATION_SEQUENCE))
#define META_IS_STARTUP_NOTIFICATION_SEQUENCE_CLASS(k) (G_TYPE_CHECK_CLASS_TYPE ((k), META_TYPE_STARTUP_NOTIFICATION_SEQUENCE))
#define META_STARTUP_NOTIFICATION_SEQUENCE_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), META_TYPE_STARTUP_NOTIFICATION_SEQUENCE, MetaStartupNotificationSequenceClass))
G_DEFINE_TYPE (MetaStartupNotification,
meta_startup_notification,
G_TYPE_OBJECT)
G_DEFINE_TYPE (MetaStartupNotificationSequence,
meta_startup_notification_sequence,
G_TYPE_OBJECT)
#ifdef HAVE_STARTUP_NOTIFICATION
enum {
PROP_SEQ_X11_0,
PROP_SEQ_X11_SEQ,
N_SEQ_X11_PROPS
};
struct _MetaStartupNotificationSequenceX11 {
MetaStartupNotificationSequence parent_instance;
SnStartupSequence *seq;
};
struct _MetaStartupNotificationSequenceX11Class {
MetaStartupNotificationSequenceClass parent_class;
};
static GParamSpec *seq_x11_props[N_SEQ_X11_PROPS];
GType meta_startup_notification_sequence_x11_get_type (void) G_GNUC_CONST;
#define META_TYPE_STARTUP_NOTIFICATION_SEQUENCE_X11 (meta_startup_notification_sequence_x11_get_type ())
#define META_STARTUP_NOTIFICATION_SEQUENCE_X11(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), META_TYPE_STARTUP_NOTIFICATION_SEQUENCE_X11, MetaStartupNotificationSequenceX11))
#define META_STARTUP_NOTIFICATION_SEQUENCE_X11_CLASS(k) (G_TYPE_CHECK_CLASS_CAST ((k), META_TYPE_STARTUP_NOTIFICATION_SEQUENCE_X11, MetaStartupNotificationSequenceX11Class))
#define META_IS_STARTUP_NOTIFICATION_SEQUENCE_X11(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), META_TYPE_STARTUP_NOTIFICATION_SEQUENCE_X11))
#define META_IS_STARTUP_NOTIFICATION_SEQUENCE_X11_CLASS(k) (G_TYPE_CHECK_CLASS_TYPE ((k), META_TYPE_STARTUP_NOTIFICATION_SEQUENCE_X11))
#define META_STARTUP_NOTIFICATION_SEQUENCE_X11_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), META_TYPE_STARTUP_NOTIFICATION_SEQUENCE_X11, MetaStartupNotificationSequenceX11Class))
G_DEFINE_TYPE (MetaStartupNotificationSequenceX11,
meta_startup_notification_sequence_x11,
META_TYPE_STARTUP_NOTIFICATION_SEQUENCE)
static void meta_startup_notification_ensure_timeout (MetaStartupNotification *sn);
#endif
static void
meta_startup_notification_update_feedback (MetaStartupNotification *sn)
{
MetaScreen *screen = sn->display->screen;
if (sn->startup_sequences != NULL)
{
meta_topic (META_DEBUG_STARTUP,
"Setting busy cursor\n");
meta_screen_set_cursor (screen, META_CURSOR_BUSY);
}
else
{
meta_topic (META_DEBUG_STARTUP,
"Setting default cursor\n");
meta_screen_set_cursor (screen, META_CURSOR_DEFAULT);
}
}
static void
meta_startup_notification_sequence_init (MetaStartupNotificationSequence *seq)
{
}
static void
meta_startup_notification_sequence_finalize (GObject *object)
{
MetaStartupNotificationSequence *seq;
seq = META_STARTUP_NOTIFICATION_SEQUENCE (object);
g_free (seq->id);
G_OBJECT_CLASS (meta_startup_notification_sequence_parent_class)->finalize (object);
}
static void
meta_startup_notification_sequence_set_property (GObject *object,
guint prop_id,
const GValue *value,
GParamSpec *pspec)
{
MetaStartupNotificationSequence *seq;
seq = META_STARTUP_NOTIFICATION_SEQUENCE (object);
switch (prop_id)
{
case PROP_SEQ_ID:
seq->id = g_value_dup_string (value);
break;
case PROP_SEQ_TIMESTAMP:
seq->timestamp = g_value_get_int64 (value);
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break;
}
}
static void
meta_startup_notification_sequence_get_property (GObject *object,
guint prop_id,
GValue *value,
GParamSpec *pspec)
{
MetaStartupNotificationSequence *seq;
seq = META_STARTUP_NOTIFICATION_SEQUENCE (object);
switch (prop_id)
{
case PROP_SEQ_ID:
g_value_set_string (value, seq->id);
break;
case PROP_SEQ_TIMESTAMP:
g_value_set_int64 (value, seq->timestamp);
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break;
}
}
static void
meta_startup_notification_sequence_class_init (MetaStartupNotificationSequenceClass *klass)
{
GObjectClass *object_class;
object_class = G_OBJECT_CLASS (klass);
object_class->finalize = meta_startup_notification_sequence_finalize;
object_class->set_property = meta_startup_notification_sequence_set_property;
object_class->get_property = meta_startup_notification_sequence_get_property;
seq_props[PROP_SEQ_ID] =
g_param_spec_string ("id",
"ID",
"ID",
NULL,
G_PARAM_READWRITE |
G_PARAM_CONSTRUCT_ONLY);
seq_props[PROP_SEQ_TIMESTAMP] =
g_param_spec_int64 ("timestamp",
"Timestamp",
"Timestamp",
G_MININT64, G_MAXINT64, 0,
G_PARAM_READWRITE |
G_PARAM_CONSTRUCT_ONLY);
g_object_class_install_properties (object_class, N_SEQ_PROPS, seq_props);
}
#ifdef HAVE_STARTUP_NOTIFICATION
static void
meta_startup_notification_sequence_complete (MetaStartupNotificationSequence *seq)
{
MetaStartupNotificationSequenceClass *klass;
klass = META_STARTUP_NOTIFICATION_SEQUENCE_GET_CLASS (seq);
if (klass->complete)
klass->complete (seq);
}
static void
meta_startup_notification_sequence_x11_complete (MetaStartupNotificationSequence *seq)
{
MetaStartupNotificationSequenceX11 *seq_x11;
seq_x11 = META_STARTUP_NOTIFICATION_SEQUENCE_X11 (seq);
sn_startup_sequence_complete (seq_x11->seq);
}
static void
meta_startup_notification_sequence_x11_finalize (GObject *object)
{
MetaStartupNotificationSequenceX11 *seq;
seq = META_STARTUP_NOTIFICATION_SEQUENCE_X11 (object);
sn_startup_sequence_unref (seq->seq);
G_OBJECT_CLASS (meta_startup_notification_sequence_x11_parent_class)->finalize (object);
}
static void
meta_startup_notification_sequence_x11_set_property (GObject *object,
guint prop_id,
const GValue *value,
GParamSpec *pspec)
{
MetaStartupNotificationSequenceX11 *seq;
seq = META_STARTUP_NOTIFICATION_SEQUENCE_X11 (object);
switch (prop_id)
{
case PROP_SEQ_X11_SEQ:
seq->seq = g_value_get_pointer (value);
sn_startup_sequence_ref (seq->seq);
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break;
}
}
static void
meta_startup_notification_sequence_x11_get_property (GObject *object,
guint prop_id,
GValue *value,
GParamSpec *pspec)
{
MetaStartupNotificationSequenceX11 *seq;
seq = META_STARTUP_NOTIFICATION_SEQUENCE_X11 (object);
switch (prop_id)
{
case PROP_SEQ_X11_SEQ:
g_value_set_pointer (value, seq->seq);
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break;
}
}
static void
meta_startup_notification_sequence_x11_init (MetaStartupNotificationSequenceX11 *seq)
{
}
static void
meta_startup_notification_sequence_x11_class_init (MetaStartupNotificationSequenceX11Class *klass)
{
MetaStartupNotificationSequenceClass *seq_class;
GObjectClass *object_class;
seq_class = META_STARTUP_NOTIFICATION_SEQUENCE_CLASS (klass);
seq_class->complete = meta_startup_notification_sequence_x11_complete;
object_class = G_OBJECT_CLASS (klass);
object_class->finalize = meta_startup_notification_sequence_x11_finalize;
object_class->set_property = meta_startup_notification_sequence_x11_set_property;
object_class->get_property = meta_startup_notification_sequence_x11_get_property;
seq_x11_props[PROP_SEQ_X11_SEQ] =
g_param_spec_pointer ("seq",
"Sequence",
"Sequence",
G_PARAM_READWRITE |
G_PARAM_CONSTRUCT_ONLY);
g_object_class_install_properties (object_class, N_SEQ_X11_PROPS,
seq_x11_props);
}
static MetaStartupNotificationSequence *
meta_startup_notification_sequence_x11_new (SnStartupSequence *seq)
{
return g_object_new (meta_startup_notification_sequence_x11_get_type (),
"id", sn_startup_sequence_get_id (seq),
"timestamp", sn_startup_sequence_get_timestamp (seq) * 1000,
"seq", seq,
NULL);
}
static void
meta_startup_notification_add_sequence_internal (MetaStartupNotification *sn,
MetaStartupNotificationSequence *seq)
{
sn->startup_sequences = g_slist_prepend (sn->startup_sequences,
g_object_ref (seq));
meta_startup_notification_ensure_timeout (sn);
meta_startup_notification_update_feedback (sn);
}
static void
collect_timed_out_foreach (void *element,
void *data)
{
MetaStartupNotificationSequence *sequence = element;
CollectTimedOutData *ctod = data;
gint64 elapsed;
elapsed = ctod->now - sequence->timestamp;
meta_topic (META_DEBUG_STARTUP,
"Sequence used %ld ms vs. %d max: %s\n",
elapsed, STARTUP_TIMEOUT, sequence->id);
if (elapsed > STARTUP_TIMEOUT)
ctod->list = g_slist_prepend (ctod->list, sequence);
}
static gboolean
startup_sequence_timeout (void *data)
{
MetaStartupNotification *sn = data;
CollectTimedOutData ctod;
GSList *l;
ctod.list = NULL;
ctod.now = g_get_monotonic_time ();
g_slist_foreach (sn->startup_sequences,
collect_timed_out_foreach,
&ctod);
for (l = ctod.list; l != NULL; l = l->next)
{
MetaStartupNotificationSequence *sequence = l->data;
meta_topic (META_DEBUG_STARTUP,
"Timed out sequence %s\n",
sequence->id);
meta_startup_notification_sequence_complete (sequence);
}
g_slist_free (ctod.list);
if (sn->startup_sequences != NULL)
{
return TRUE;
}
else
{
/* remove */
sn->startup_sequence_timeout = 0;
return FALSE;
}
}
static void
meta_startup_notification_ensure_timeout (MetaStartupNotification *sn)
{
if (sn->startup_sequence_timeout != 0)
return;
/* our timeout just polls every second, instead of bothering
* to compute exactly when we may next time out
*/
sn->startup_sequence_timeout = g_timeout_add_seconds (1,
startup_sequence_timeout,
sn);
g_source_set_name_by_id (sn->startup_sequence_timeout,
"[mutter] startup_sequence_timeout");
}
#endif
static void
meta_startup_notification_remove_sequence_internal (MetaStartupNotification *sn,
MetaStartupNotificationSequence *seq)
{
sn->startup_sequences = g_slist_remove (sn->startup_sequences, seq);
meta_startup_notification_update_feedback (sn);
if (sn->startup_sequences == NULL &&
sn->startup_sequence_timeout != 0)
{
g_source_remove (sn->startup_sequence_timeout);
sn->startup_sequence_timeout = 0;
}
g_object_unref (seq);
}
static MetaStartupNotificationSequence *
meta_startup_notification_lookup_sequence (MetaStartupNotification *sn,
const gchar *id)
{
MetaStartupNotificationSequence *seq;
GSList *l;
for (l = sn->startup_sequences; l; l = l->next)
{
seq = l->data;
if (g_str_equal (seq->id, id))
return l->data;
}
return NULL;
}
static void
meta_startup_notification_init (MetaStartupNotification *sn)
{
}
static void
meta_startup_notification_finalize (GObject *object)
{
MetaStartupNotification *sn = META_STARTUP_NOTIFICATION (object);
sn_monitor_context_unref (sn->sn_context);
G_OBJECT_CLASS (meta_startup_notification_parent_class)->finalize (object);
}
static void
meta_startup_notification_set_property (GObject *object,
guint prop_id,
const GValue *value,
GParamSpec *pspec)
{
MetaStartupNotification *sn = META_STARTUP_NOTIFICATION (object);
switch (prop_id)
{
case PROP_SN_DISPLAY:
sn->display = g_value_get_object (value);
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break;
}
}
static void
meta_startup_notification_get_property (GObject *object,
guint prop_id,
GValue *value,
GParamSpec *pspec)
{
MetaStartupNotification *sn = META_STARTUP_NOTIFICATION (object);
switch (prop_id)
{
case PROP_SN_DISPLAY:
g_value_set_object (value, sn->display);
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break;
}
}
#ifdef HAVE_STARTUP_NOTIFICATION
static void
sn_error_trap_push (SnDisplay *sn_display,
Display *xdisplay)
{
MetaDisplay *display;
display = meta_display_for_x_display (xdisplay);
if (display != NULL)
meta_error_trap_push (display);
}
static void
sn_error_trap_pop (SnDisplay *sn_display,
Display *xdisplay)
{
MetaDisplay *display;
display = meta_display_for_x_display (xdisplay);
if (display != NULL)
meta_error_trap_pop (display);
}
static void
meta_startup_notification_sn_event (SnMonitorEvent *event,
void *user_data)
{
MetaStartupNotification *sn = user_data;
MetaStartupNotificationSequence *seq;
SnStartupSequence *sequence;
sequence = sn_monitor_event_get_startup_sequence (event);
sn_startup_sequence_ref (sequence);
switch (sn_monitor_event_get_type (event))
{
case SN_MONITOR_EVENT_INITIATED:
{
const char *wmclass;
wmclass = sn_startup_sequence_get_wmclass (sequence);
meta_topic (META_DEBUG_STARTUP,
"Received startup initiated for %s wmclass %s\n",
sn_startup_sequence_get_id (sequence),
wmclass ? wmclass : "(unset)");
seq = meta_startup_notification_sequence_x11_new (sequence);
meta_startup_notification_add_sequence_internal (sn, seq);
g_object_unref (seq);
}
break;
case SN_MONITOR_EVENT_COMPLETED:
{
meta_topic (META_DEBUG_STARTUP,
"Received startup completed for %s\n",
sn_startup_sequence_get_id (sequence));
meta_startup_notification_remove_sequence (sn, sn_startup_sequence_get_id (sequence));
}
break;
case SN_MONITOR_EVENT_CHANGED:
meta_topic (META_DEBUG_STARTUP,
"Received startup changed for %s\n",
sn_startup_sequence_get_id (sequence));
break;
case SN_MONITOR_EVENT_CANCELED:
meta_topic (META_DEBUG_STARTUP,
"Received startup canceled for %s\n",
sn_startup_sequence_get_id (sequence));
break;
}
g_signal_emit (sn, sn_signals[SN_CHANGED], 0, sequence);
sn_startup_sequence_unref (sequence);
}
#endif
static void
meta_startup_notification_constructed (GObject *object)
{
MetaStartupNotification *sn = META_STARTUP_NOTIFICATION (object);
g_assert (sn->display != NULL);
#ifdef HAVE_STARTUP_NOTIFICATION
sn->sn_display = sn_display_new (sn->display->xdisplay,
sn_error_trap_push,
sn_error_trap_pop);
sn->sn_context =
sn_monitor_context_new (sn->sn_display,
sn->display->screen->number,
meta_startup_notification_sn_event,
sn,
NULL);
sn->startup_sequences = NULL;
sn->startup_sequence_timeout = 0;
#endif
}
static void
meta_startup_notification_class_init (MetaStartupNotificationClass *klass)
{
GObjectClass *object_class = G_OBJECT_CLASS (klass);
object_class->constructed = meta_startup_notification_constructed;
object_class->finalize = meta_startup_notification_finalize;
object_class->set_property = meta_startup_notification_set_property;
object_class->get_property = meta_startup_notification_get_property;
sn_props[PROP_SN_DISPLAY] =
g_param_spec_object ("display",
"Display",
"Display",
META_TYPE_DISPLAY,
G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY |
G_PARAM_STATIC_STRINGS);
sn_signals[SN_CHANGED] =
g_signal_new ("changed",
META_TYPE_STARTUP_NOTIFICATION,
G_SIGNAL_RUN_LAST,
0, NULL, NULL, NULL,
G_TYPE_NONE, 1, G_TYPE_POINTER);
g_object_class_install_properties (object_class, N_SN_PROPS, sn_props);
}
MetaStartupNotification *
meta_startup_notification_get (MetaDisplay *display)
{
static MetaStartupNotification *notification = NULL;
if (!notification)
notification = g_object_new (META_TYPE_STARTUP_NOTIFICATION,
"display", display,
NULL);
return notification;
}
void
meta_startup_notification_remove_sequence (MetaStartupNotification *sn,
const gchar *id)
{
MetaStartupNotificationSequence *seq;
seq = meta_startup_notification_lookup_sequence (sn, id);
if (seq)
meta_startup_notification_remove_sequence_internal (sn, seq);
}
gboolean
meta_startup_notification_handle_xevent (MetaStartupNotification *sn,
XEvent *xevent)
{
#ifdef HAVE_STARTUP_NOTIFICATION
return sn_display_process_event (sn->sn_display, xevent);
#endif
return FALSE;
}
GSList *
meta_startup_notification_get_sequences (MetaStartupNotification *sn)
{
GSList *l, *sequences = NULL;
/* We return a list of SnStartupSequences here */
for (l = sn->startup_sequences; l; l = l->next)
{
#ifdef HAVE_STARTUP_NOTIFICATION
MetaStartupNotificationSequenceX11 *seq_x11;
if (!META_IS_STARTUP_NOTIFICATION_SEQUENCE_X11 (l->data))
continue;
seq_x11 = META_STARTUP_NOTIFICATION_SEQUENCE_X11 (l->data);
sequences = g_slist_prepend (sequences, seq_x11->seq);
#endif
}
return sequences;
}

View File

@@ -7417,14 +7417,14 @@ meta_window_set_transient_for (MetaWindow *window,
}
}
/* update stacking constraints */
if (!window->override_redirect)
meta_stack_update_transient (window->screen->stack, window);
/* We know this won't create a reference cycle because we check for loops */
g_clear_object (&window->transient_for);
window->transient_for = parent ? g_object_ref (parent) : NULL;
/* update stacking constraints */
if (!window->override_redirect)
meta_stack_update_transient (window->screen->stack, window);
/* possibly change its group. We treat being a window's transient as
* equivalent to making it your group leader, to work around shortcomings
* in programs such as xmms-- see #328211.
@@ -7435,7 +7435,7 @@ meta_window_set_transient_for (MetaWindow *window,
meta_window_group_leader_changed (window);
if (!window->constructing && !window->override_redirect)
meta_window_queue (window, META_QUEUE_MOVE_RESIZE);
meta_window_queue (window, META_QUEUE_MOVE_RESIZE | META_QUEUE_CALC_SHOWING);
if (meta_window_appears_focused (window) && window->transient_for != NULL)
meta_window_propagate_focus_appearance (window, TRUE);
@@ -7707,6 +7707,22 @@ meta_window_handle_ungrabbed_event (MetaWindow *window,
if (window->override_redirect)
return;
/* Don't focus panels--they must explicitly request focus.
* See bug 160470
*/
if (window->type != META_WINDOW_DOCK)
{
meta_topic (META_DEBUG_FOCUS,
"Focusing %s due to button %u press (display.c)\n",
window->desc, button);
meta_window_focus (window, event->any.time);
}
else
/* However, do allow terminals to lose focus due to new
* window mappings after the user clicks on a panel.
*/
display->allow_terminal_deactivation = TRUE;
/* We have three passive button grabs:
* - on any button, without modifiers => focuses and maybe raises the window
* - on resize button, with modifiers => start an interactive resizing
@@ -7741,25 +7757,6 @@ meta_window_handle_ungrabbed_event (MetaWindow *window,
else
meta_topic (META_DEBUG_FOCUS,
"Not raising window on click due to don't-raise-on-click option\n");
/* Don't focus panels--they must explicitly request focus.
* See bug 160470
*/
if (window->type != META_WINDOW_DOCK)
{
meta_topic (META_DEBUG_FOCUS,
"Focusing %s due to unmodified button %u press (display.c)\n",
window->desc, button);
meta_window_focus (window, event->any.time);
}
else
/* However, do allow terminals to lose focus due to new
* window mappings after the user clicks on a panel.
*/
display->allow_terminal_deactivation = TRUE;
meta_verbose ("Allowing events time %u\n",
(unsigned int)event->any.time);
}
else if (is_window_grab && (int) button == meta_prefs_get_mouse_button_resize ())
{

View File

@@ -0,0 +1,14 @@
new_client 1 wayland
create 1/1
show 1/1
create 1/2
show 1/2
wait
assert_stacking 1/1 1/2
set_parent 1/1 2
wait
assert_stacking 1/2 1/1
local_activate 1/2
assert_stacking 1/2 1/1

View File

@@ -41,6 +41,16 @@ lookup_window (const char *window_id)
return window;
}
static void
on_after_paint (GdkFrameClock *clock,
GMainLoop *loop)
{
g_signal_handlers_disconnect_by_func (clock,
(gpointer) on_after_paint,
loop);
g_main_loop_quit (loop);
}
static void
process_line (const char *line)
{
@@ -126,6 +136,31 @@ process_line (const char *line)
}
}
else if (strcmp (argv[0], "set_parent") == 0)
{
if (argc != 3)
{
g_print ("usage: menu <window-id> <parent-id>");
goto out;
}
GtkWidget *window = lookup_window (argv[1]);
if (!window)
{
g_print ("unknown window %s", argv[1]);
goto out;
}
GtkWidget *parent_window = lookup_window (argv[2]);
if (!parent_window)
{
g_print ("unknown parent window %s", argv[2]);
goto out;
}
gtk_window_set_transient_for (GTK_WINDOW (window),
GTK_WINDOW (parent_window));
}
else if (strcmp (argv[0], "show") == 0)
{
if (argc != 2)
@@ -135,10 +170,25 @@ process_line (const char *line)
}
GtkWidget *window = lookup_window (argv[1]);
GdkWindow *gdk_window = gtk_widget_get_window (window);
if (!window)
goto out;
gtk_widget_show (window);
/* When a Wayland client, we cannot be really sure that the window has
* been mappable until after we have painted. So, in order to have the
* test runner rely on the "show" command to have done what the client
* needs to do in order for a window to be mappable compositor side, lets
* wait with returning until after the first frame.
*/
GdkFrameClock *frame_clock = gdk_window_get_frame_clock (gdk_window);
GMainLoop *loop = g_main_loop_new (NULL, FALSE);
g_signal_connect (frame_clock, "after-paint",
G_CALLBACK (on_after_paint),
loop);
g_main_loop_run (loop);
g_main_loop_unref (loop);
}
else if (strcmp (argv[0], "hide") == 0)
{

View File

@@ -767,6 +767,23 @@ test_case_do (TestCase *test,
NULL))
return FALSE;
}
else if (strcmp (argv[0], "set_parent") == 0)
{
if (argc != 3)
BAD_COMMAND("usage: %s <client-id>/<window-id> <parent-window-id>",
argv[0]);
TestClient *client;
const char *window_id;
if (!test_case_parse_window_id (test, argv[1], &client, &window_id, error))
return FALSE;
if (!test_client_do (client, error,
"set_parent", window_id,
argv[2],
NULL))
return FALSE;
}
else if (strcmp (argv[0], "show") == 0 ||
strcmp (argv[0], "hide") == 0 ||
strcmp (argv[0], "activate") == 0 ||

View File

@@ -447,7 +447,7 @@ meta_frames_new (int screen_number)
}
static const char *
get_theme_variant_override (MetaFrames *frames)
get_global_theme_variant (MetaFrames *frames)
{
GdkScreen *screen = gtk_widget_get_screen (GTK_WIDGET (frames));
GtkSettings *settings = gtk_settings_get_for_screen (screen);
@@ -474,19 +474,15 @@ meta_ui_frame_attach_style (MetaUIFrame *frame)
{
MetaFrames *frames = frame->frames;
const char *variant;
const char *variant_override;
if (frame->style_info != NULL)
meta_style_info_unref (frame->style_info);
variant_override = get_theme_variant_override (frame->frames);
variant = frame->meta_window->gtk_theme_variant;
if (variant == NULL)
variant = get_global_theme_variant (frame->frames);;
if (variant_override)
variant = variant_override;
else
variant = frame->meta_window->gtk_theme_variant;
if (variant == NULL || strcmp(variant, "normal") == 0)
if (variant == NULL || *variant == '\0')
frame->style_info = meta_style_info_ref (frames->normal_style);
else
frame->style_info = meta_style_info_ref (meta_frames_get_theme_variant (frames,
@@ -1068,7 +1064,10 @@ handle_button_press_event (MetaUIFrame *frame,
control = get_control (frame, event->x, event->y);
/* focus on click, even if click was on client area */
/* don't do the rest of this if on client area */
if (control == META_FRAME_CONTROL_CLIENT_AREA)
return FALSE; /* not on the frame, just passed through from client */
if (event->button == 1 &&
!(control == META_FRAME_CONTROL_MINIMIZE ||
control == META_FRAME_CONTROL_DELETE ||
@@ -1080,10 +1079,6 @@ handle_button_press_event (MetaUIFrame *frame,
meta_window_focus (frame->meta_window, event->time);
}
/* don't do the rest of this if on client area */
if (control == META_FRAME_CONTROL_CLIENT_AREA)
return FALSE; /* not on the frame, just passed through from client */
/* We want to shade even if we have a GrabOp, since we'll have a move grab
* if we double click the titlebar.
*/
@@ -1367,6 +1362,10 @@ meta_ui_frame_get_mask (MetaUIFrame *frame,
borders.invisible.left / scale,
borders.invisible.top / scale,
frame_rect.width / scale, frame_rect.height / scale);
gtk_render_background (frame->style_info->styles[META_STYLE_ELEMENT_TITLEBAR], cr,
borders.invisible.left / scale,
borders.invisible.top / scale,
frame_rect.width / scale, borders.total.top / scale);
}
/* XXX -- this is disgusting. Find a better approach here.

View File

@@ -53,6 +53,8 @@ typedef struct _MetaFrameGeometry MetaFrameGeometry;
**/
struct _MetaFrameLayout
{
/** Invisible border required by the theme */
GtkBorder invisible_border;
/** Border/padding of the entire frame */
GtkBorder frame_border;
/** Border/padding of the titlebar region */
@@ -60,6 +62,16 @@ struct _MetaFrameLayout
/** Border/padding of titlebar buttons */
GtkBorder button_border;
/** Margin of title */
GtkBorder title_margin;
/** Margin of titlebar buttons */
GtkBorder button_margin;
/** Min size of titlebar region */
GtkRequisition titlebar_min_size;
/** Min size of titlebar buttons */
GtkRequisition button_min_size;
/** Size of images in buttons */
guint icon_size;
@@ -157,6 +169,7 @@ typedef enum
typedef enum
{
META_STYLE_ELEMENT_WINDOW,
META_STYLE_ELEMENT_FRAME,
META_STYLE_ELEMENT_TITLEBAR,
META_STYLE_ELEMENT_TITLE,

View File

@@ -79,10 +79,14 @@ meta_frame_layout_get_borders (const MetaFrameLayout *layout,
if (!layout->has_title)
text_height = 0;
else
text_height = layout->title_margin.top + text_height + layout->title_margin.bottom;
buttons_height = layout->icon_size +
layout->button_border.top + layout->button_border.bottom;
content_height = MAX (buttons_height, text_height) +
buttons_height = MAX ((int)layout->icon_size, layout->button_min_size.height) +
layout->button_margin.top + layout->button_border.top +
layout->button_margin.bottom + layout->button_border.bottom;
content_height = MAX (buttons_height, text_height);
content_height = MAX (content_height, layout->titlebar_min_size.height) +
layout->titlebar_border.top + layout->titlebar_border.bottom;
borders->visible.top = layout->frame_border.top + content_height;
@@ -90,23 +94,28 @@ meta_frame_layout_get_borders (const MetaFrameLayout *layout,
borders->visible.right = layout->frame_border.right;
borders->visible.bottom = layout->frame_border.bottom;
borders->invisible = layout->invisible_border;
draggable_borders = meta_prefs_get_draggable_border_width ();
if (flags & META_FRAME_ALLOWS_HORIZONTAL_RESIZE)
{
borders->invisible.left = MAX (0, draggable_borders - borders->visible.left);
borders->invisible.right = MAX (0, draggable_borders - borders->visible.right);
borders->invisible.left = MAX (borders->invisible.left,
draggable_borders - borders->visible.left);
borders->invisible.right = MAX (borders->invisible.right,
draggable_borders - borders->visible.right);
}
if (flags & META_FRAME_ALLOWS_VERTICAL_RESIZE)
{
borders->invisible.bottom = MAX (0, draggable_borders - borders->visible.bottom);
borders->invisible.bottom = MAX (borders->invisible.bottom,
draggable_borders - borders->visible.bottom);
/* borders.visible.top is the height of the *title bar*. We can't do the same
* algorithm here, titlebars are expectedly much bigger. Just subtract a couple
* pixels to get a proper feel. */
if (type != META_FRAME_TYPE_ATTACHED)
borders->invisible.top = MAX (0, draggable_borders - 2);
borders->invisible.top = MAX (borders->invisible.top, draggable_borders - 2);
}
borders->total.left = borders->invisible.left + borders->visible.left;
@@ -234,6 +243,16 @@ get_padding_and_border (GtkStyleContext *style,
border->bottom += tmp.bottom;
}
static void
get_min_size (GtkStyleContext *style,
GtkRequisition *requisition)
{
gtk_style_context_get (style, gtk_style_context_get_state (style),
"min-width", &requisition->width,
"min-height", &requisition->height,
NULL);
}
static void
scale_border (GtkBorder *border,
double factor)
@@ -251,6 +270,8 @@ meta_frame_layout_sync_with_style (MetaFrameLayout *layout,
{
GtkStyleContext *style;
GtkBorder border;
GtkRequisition requisition;
GdkRectangle clip_rect;
int border_radius, max_radius;
meta_style_info_set_flags (style_info, flags);
@@ -259,6 +280,12 @@ meta_frame_layout_sync_with_style (MetaFrameLayout *layout,
get_padding_and_border (style, &layout->frame_border);
scale_border (&layout->frame_border, layout->title_scale);
gtk_render_background_get_clip (style, 0, 0, 0, 0, &clip_rect);
layout->invisible_border.left = -clip_rect.x;
layout->invisible_border.right = clip_rect.width + clip_rect.x;
layout->invisible_border.top = -clip_rect.y;
layout->invisible_border.bottom = clip_rect.height + clip_rect.y;
if (layout->hide_buttons)
layout->icon_size = 0;
@@ -281,14 +308,25 @@ meta_frame_layout_sync_with_style (MetaFrameLayout *layout,
max_radius = MIN (layout->frame_border.bottom, layout->frame_border.right);
layout->bottom_right_corner_rounded_radius = MAX (border_radius, max_radius);
get_min_size (style, &layout->titlebar_min_size);
get_padding_and_border (style, &layout->titlebar_border);
scale_border (&layout->titlebar_border, layout->title_scale);
style = style_info->styles[META_STYLE_ELEMENT_TITLE];
gtk_style_context_get_margin (style, gtk_style_context_get_state (style),
&layout->title_margin);
scale_border (&layout->title_margin, layout->title_scale);
style = style_info->styles[META_STYLE_ELEMENT_BUTTON];
get_min_size (style, &layout->button_min_size);
get_padding_and_border (style, &layout->button_border);
scale_border (&layout->button_border, layout->title_scale);
gtk_style_context_get_margin (style, gtk_style_context_get_state (style),
&layout->button_margin);
scale_border (&layout->button_margin, layout->title_scale);
style = style_info->styles[META_STYLE_ELEMENT_IMAGE];
get_min_size (style, &requisition);
get_padding_and_border (style, &border);
scale_border (&border, layout->title_scale);
@@ -296,6 +334,18 @@ meta_frame_layout_sync_with_style (MetaFrameLayout *layout,
layout->button_border.right += border.right;
layout->button_border.top += border.top;
layout->button_border.bottom += border.bottom;
gtk_style_context_get_margin (style, gtk_style_context_get_state (style),
&border);
layout->button_border.left += border.left;
layout->button_border.right += border.right;
layout->button_border.top += border.top;
layout->button_border.bottom += border.bottom;
layout->button_min_size.width = MAX(layout->button_min_size.width,
requisition.width);
layout->button_min_size.height = MAX(layout->button_min_size.height,
requisition.height);
}
static void
@@ -359,9 +409,9 @@ meta_frame_layout_calc_geometry (MetaFrameLayout *layout,
(fgeom->content_border.right + borders.invisible.right);
content_height = borders.visible.top - fgeom->content_border.top - fgeom->content_border.bottom;
button_width = layout->icon_size +
button_width = MAX ((int)layout->icon_size, layout->button_min_size.width) +
layout->button_border.left + layout->button_border.right;
button_height = layout->icon_size +
button_height = MAX ((int)layout->icon_size, layout->button_min_size.height) +
layout->button_border.top + layout->button_border.bottom;
button_width *= scale;
button_height *= scale;
@@ -422,11 +472,15 @@ meta_frame_layout_calc_geometry (MetaFrameLayout *layout,
space_used_by_buttons = 0;
space_used_by_buttons += layout->button_margin.left * scale * n_left;
space_used_by_buttons += button_width * n_left;
space_used_by_buttons += layout->button_margin.right * scale * n_left;
space_used_by_buttons += (button_width * 0.75) * n_left_spacers;
space_used_by_buttons += layout->titlebar_spacing * scale * MAX (n_left - 1, 0);
space_used_by_buttons += layout->button_margin.left * scale * n_right;
space_used_by_buttons += button_width * n_right;
space_used_by_buttons += layout->button_margin.right * scale * n_right;
space_used_by_buttons += (button_width * 0.75) * n_right_spacers;
space_used_by_buttons += layout->titlebar_spacing * scale * MAX (n_right - 1, 0);
@@ -496,6 +550,8 @@ meta_frame_layout_calc_geometry (MetaFrameLayout *layout,
if (x < 0) /* if we go negative, leave the buttons we don't get to as 0-width */
break;
x -= layout->button_margin.right * scale;
rect = right_func_rects[i];
rect->visible.x = x - button_width;
if (right_buttons_has_spacer[i])
@@ -521,7 +577,7 @@ meta_frame_layout_calc_geometry (MetaFrameLayout *layout,
else
g_memmove (&(rect->clickable), &(rect->visible), sizeof(rect->clickable));
x = rect->visible.x;
x = rect->visible.x - layout->button_margin.left * scale;
if (i > 0)
x -= layout->titlebar_spacing;
@@ -540,6 +596,8 @@ meta_frame_layout_calc_geometry (MetaFrameLayout *layout,
{
MetaButtonSpace *rect;
x += layout->button_margin.left * scale;
rect = left_func_rects[i];
rect->visible.x = x;
@@ -566,7 +624,7 @@ meta_frame_layout_calc_geometry (MetaFrameLayout *layout,
else
g_memmove (&(rect->clickable), &(rect->visible), sizeof(rect->clickable));
x = rect->visible.x + rect->visible.width;
x = rect->visible.x + rect->visible.width + layout->button_margin.right * scale;
if (i < n_left - 1)
x += layout->titlebar_spacing * scale;
if (left_buttons_has_spacer[i])
@@ -987,15 +1045,20 @@ meta_theme_create_style_info (GdkScreen *screen,
style_info = g_new0 (MetaStyleInfo, 1);
style_info->refcount = 1;
style_info->styles[META_STYLE_ELEMENT_FRAME] =
style_info->styles[META_STYLE_ELEMENT_WINDOW] =
create_style_context (META_TYPE_FRAMES,
NULL,
provider,
"decoration",
"window",
GTK_STYLE_CLASS_BACKGROUND,
"window-frame",
"ssd",
NULL);
style_info->styles[META_STYLE_ELEMENT_FRAME] =
create_style_context (META_TYPE_FRAMES,
style_info->styles[META_STYLE_ELEMENT_WINDOW],
provider,
"decoration",
NULL);
style_info->styles[META_STYLE_ELEMENT_TITLEBAR] =
create_style_context (GTK_TYPE_HEADER_BAR,
style_info->styles[META_STYLE_ELEMENT_FRAME],

View File

@@ -0,0 +1,707 @@
/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
/*
* Copyright (C) 2015 Red Hat
*
* 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.
*
* Written by:
* Jonas Ådahl <jadahl@gmail.com>
*/
#include "config.h"
#include "wayland/meta-pointer-confinement-wayland.h"
#include <glib-object.h>
#include <cairo.h>
#include "backends/meta-backend-private.h"
#include "core/meta-border.h"
#include "wayland/meta-wayland-seat.h"
#include "wayland/meta-wayland-pointer.h"
#include "wayland/meta-wayland-pointer-constraints.h"
#include "wayland/meta-wayland-surface.h"
#include "backends/meta-pointer-constraint.h"
#include "compositor/meta-surface-actor-wayland.h"
struct _MetaPointerConfinementWayland
{
MetaPointerConstraint parent;
MetaWaylandPointerConstraint *constraint;
};
typedef struct _MetaBox
{
int x1;
int y1;
int x2;
int y2;
} MetaBox;
G_DEFINE_TYPE (MetaPointerConfinementWayland, meta_pointer_confinement_wayland,
META_TYPE_POINTER_CONSTRAINT);
static MetaBorder *
add_border (GArray *borders,
float x1, float y1,
float x2, float y2,
MetaBorderMotionDirection blocking_directions)
{
MetaBorder border;
border = (MetaBorder) {
.line = (MetaLine2) {
.a = (MetaVector2) {
.x = x1,
.y = y1,
},
.b = (MetaVector2) {
.x = x2,
.y = y2,
},
},
.blocking_directions = blocking_directions,
};
g_array_append_val (borders, border);
return &g_array_index (borders, MetaBorder, borders->len - 1);
}
static gint
compare_lines_x (gconstpointer a, gconstpointer b)
{
const MetaBorder *border_a = a;
const MetaBorder *border_b = b;
if (border_a->line.a.x == border_b->line.a.x)
return border_a->line.b.x < border_b->line.b.x;
else
return border_a->line.a.x > border_b->line.a.x;
}
static void
add_non_overlapping_edges (MetaBox *boxes,
unsigned int band_above_start,
unsigned int band_below_start,
unsigned int band_below_end,
GArray *borders)
{
unsigned int i;
GArray *band_merge;
MetaBorder *border;
MetaBorder *prev_border;
MetaBorder *new_border;
band_merge = g_array_new (FALSE, FALSE, sizeof *border);
/* Add bottom band of previous row, and top band of current row, and
* sort them so lower left x coordinate comes first. If there are two
* borders with the same left x coordinate, the wider one comes first.
*/
for (i = band_above_start; i < band_below_start; i++)
{
MetaBox *box = &boxes[i];
add_border (band_merge, box->x1, box->y2, box->x2, box->y2,
META_BORDER_MOTION_DIRECTION_POSITIVE_Y);
}
for (i = band_below_start; i < band_below_end; i++)
{
MetaBox *box= &boxes[i];
add_border (band_merge, box->x1, box->y1, box->x2, box->y1,
META_BORDER_MOTION_DIRECTION_NEGATIVE_Y);
}
g_array_sort (band_merge, compare_lines_x);
/* Combine the two combined bands so that any overlapping border is
* eliminated. */
prev_border = NULL;
for (i = 0; i < band_merge->len; i++)
{
border = &g_array_index (band_merge, MetaBorder, i);
g_assert (border->line.a.y == border->line.b.y);
g_assert (!prev_border ||
prev_border->line.a.y == border->line.a.y);
g_assert (!prev_border ||
(prev_border->line.a.x != border->line.a.x ||
prev_border->line.b.x != border->line.b.x));
g_assert (!prev_border ||
prev_border->line.a.x <= border->line.a.x);
if (prev_border &&
prev_border->line.a.x == border->line.a.x)
{
/*
* ------------ +
* ------- =
* [ ]-----
*/
prev_border->line.a.x = border->line.b.x;
}
else if (prev_border &&
prev_border->line.b.x == border->line.b.x)
{
/*
* ------------ +
* ------ =
* ------[ ]
*/
prev_border->line.b.x = border->line.a.x;
}
else if (prev_border &&
prev_border->line.b.x == border->line.a.x)
{
/*
* -------- +
* ------ =
* --------------
*/
prev_border->line.b.x = border->line.b.x;
}
else if (prev_border &&
prev_border->line.b.x >= border->line.a.x)
{
/*
* --------------- +
* ------ =
* -----[ ]----
*/
new_border = add_border (borders,
border->line.b.x,
border->line.b.y,
prev_border->line.b.x,
prev_border->line.b.y,
prev_border->blocking_directions);
prev_border->line.b.x = border->line.a.x;
prev_border = new_border;
}
else
{
g_assert (!prev_border ||
prev_border->line.b.x < border->line.a.x);
/*
* First border or non-overlapping.
*
* ----- +
* ----- =
* ----- -----
*/
g_array_append_val (borders, *border);
prev_border = &g_array_index (borders, MetaBorder, borders->len - 1);
}
}
g_array_free (band_merge, FALSE);
}
static void
add_band_bottom_edges (MetaBox *boxes,
int band_start,
int band_end,
GArray *borders)
{
int i;
for (i = band_start; i < band_end; i++)
{
add_border (borders,
boxes[i].x1, boxes[i].y2,
boxes[i].x2, boxes[i].y2,
META_BORDER_MOTION_DIRECTION_POSITIVE_Y);
}
}
static void
region_to_outline (cairo_region_t *region,
GArray *borders)
{
MetaBox *boxes;
int num_boxes;
int i;
int top_most, bottom_most;
int current_roof;
int prev_top;
int band_start, prev_band_start;
/*
* Remove any overlapping lines from the set of rectangles. Note that
* pixman regions are grouped as rows of rectangles, where rectangles
* in one row never touch or overlap and are all of the same height.
*
* -------- --- -------- ---
* | | | | | | | |
* ----------====---- --- ----------- ----- ---
* | | => | |
* ----==========--------- ----- ----------
* | | | |
* ------------------- -------------------
*
*/
num_boxes = cairo_region_num_rectangles (region);
boxes = g_new (MetaBox, num_boxes);
for (i = 0; i < num_boxes; i++)
{
cairo_rectangle_int_t rect;
cairo_region_get_rectangle (region, i, &rect);
boxes[i] = (MetaBox) {
.x1 = rect.x,
.y1 = rect.y,
.x2 = rect.x + rect.width,
.y2 = rect.y + rect.height,
};
}
prev_top = 0;
top_most = boxes[0].y1;
current_roof = top_most;
bottom_most = boxes[num_boxes - 1].y2;
band_start = 0;
prev_band_start = 0;
for (i = 0; i < num_boxes; i++)
{
/* Detect if there is a vertical empty space, and add the lower
* level of the previous band if so was the case. */
if (i > 0 &&
boxes[i].y1 != prev_top &&
boxes[i].y1 != boxes[i - 1].y2)
{
current_roof = boxes[i].y1;
add_band_bottom_edges (boxes,
band_start,
i,
borders);
}
/* Special case adding the last band, since it won't be handled
* by the band change detection below. */
if (boxes[i].y1 != current_roof && i == num_boxes - 1)
{
if (boxes[i].y1 != prev_top)
{
/* The last band is a single box, so we don't
* have a prev_band_start to tell us when the
* previous band started. */
add_non_overlapping_edges (boxes,
band_start,
i,
i + 1,
borders);
}
else
{
add_non_overlapping_edges (boxes,
prev_band_start,
band_start,
i + 1,
borders);
}
}
/* Detect when passing a band and combine the top border of the
* just passed band with the bottom band of the previous band.
*/
if (boxes[i].y1 != top_most && boxes[i].y1 != prev_top)
{
/* Combine the two passed bands. */
if (prev_top != current_roof)
{
add_non_overlapping_edges (boxes,
prev_band_start,
band_start,
i,
borders);
}
prev_band_start = band_start;
band_start = i;
}
/* Add the top border if the box is part of the current roof. */
if (boxes[i].y1 == current_roof)
{
add_border (borders,
boxes[i].x1, boxes[i].y1,
boxes[i].x2, boxes[i].y1,
META_BORDER_MOTION_DIRECTION_NEGATIVE_Y);
}
/* Add the bottom border of the last band. */
if (boxes[i].y2 == bottom_most)
{
add_border (borders,
boxes[i].x1, boxes[i].y2,
boxes[i].x2, boxes[i].y2,
META_BORDER_MOTION_DIRECTION_POSITIVE_Y);
}
/* Always add the left border. */
add_border (borders,
boxes[i].x1, boxes[i].y1,
boxes[i].x1, boxes[i].y2,
META_BORDER_MOTION_DIRECTION_NEGATIVE_X);
/* Always add the right border. */
add_border (borders,
boxes[i].x2, boxes[i].y1,
boxes[i].x2, boxes[i].y2,
META_BORDER_MOTION_DIRECTION_POSITIVE_X);
prev_top = boxes[i].y1;
}
g_free (boxes);
}
static MetaBorder *
get_closest_border (GArray *borders,
MetaLine2 *motion,
uint32_t directions)
{
MetaBorder *border;
MetaVector2 intersection;
MetaVector2 delta;
float distance_2;
MetaBorder *closest_border = NULL;
float closest_distance_2 = DBL_MAX;
unsigned int i;
for (i = 0; i < borders->len; i++)
{
border = &g_array_index (borders, MetaBorder, i);
if (!meta_border_is_blocking_directions (border, directions))
continue;
if (!meta_line2_intersects_with (&border->line, motion, &intersection))
continue;
delta = meta_vector2_subtract (intersection, motion->a);
distance_2 = delta.x*delta.x + delta.y*delta.y;
if (distance_2 < closest_distance_2)
{
closest_border = border;
closest_distance_2 = distance_2;
}
}
return closest_border;
}
static void
clamp_to_border (MetaBorder *border,
MetaLine2 *motion,
uint32_t *motion_dir)
{
/*
* When clamping either rightward or downward motions, the motion needs to be
* clamped so that the destination coordinate does not end up on the border
* (see weston_pointer_clamp_event_to_region). Do this by clamping such
* motions to the border minus the smallest possible wl_fixed_t value.
*
* When clamping in either leftward or upward motion, the resulting coordinate
* needs to be clamped so that it is enough on the inside to avoid the
* inaccuracies of clutter's stage to actor transformation algorithm (the one
* used in clutter_actor_transform_stage_point) to make it end up outside the
* next motion. It also needs to be clamped so that to the wl_fixed_t
* coordinate may still be right on the border (i.e. at .0). Testing shows
* that the smallest wl_fixed_t value divided by 10 is small enough to make
* the wl_fixed_t coordinate .0 and large enough to avoid the inaccuracies of
* clutters transform algorithm.
*/
if (meta_border_is_horizontal (border))
{
if (*motion_dir & META_BORDER_MOTION_DIRECTION_POSITIVE_Y)
motion->b.y = border->line.a.y - wl_fixed_to_double (1);
else
motion->b.y = border->line.a.y + wl_fixed_to_double (1) / 10;
*motion_dir &= ~(META_BORDER_MOTION_DIRECTION_POSITIVE_Y |
META_BORDER_MOTION_DIRECTION_NEGATIVE_Y);
}
else
{
if (*motion_dir & META_BORDER_MOTION_DIRECTION_POSITIVE_X)
motion->b.x = border->line.a.x - wl_fixed_to_double (1);
else
motion->b.x = border->line.a.x + wl_fixed_to_double (1) / 10;
*motion_dir &= ~(META_BORDER_MOTION_DIRECTION_POSITIVE_X |
META_BORDER_MOTION_DIRECTION_NEGATIVE_X);
}
}
static uint32_t
get_motion_directions (MetaLine2 *motion)
{
uint32_t directions = 0;
if (motion->a.x < motion->b.x)
directions |= META_BORDER_MOTION_DIRECTION_POSITIVE_X;
else if (motion->a.x > motion->b.x)
directions |= META_BORDER_MOTION_DIRECTION_NEGATIVE_X;
if (motion->a.y < motion->b.y)
directions |= META_BORDER_MOTION_DIRECTION_POSITIVE_Y;
else if (motion->a.y > motion->b.y)
directions |= META_BORDER_MOTION_DIRECTION_NEGATIVE_Y;
return directions;
}
static void
meta_pointer_confinement_wayland_constrain (MetaPointerConstraint *constraint,
ClutterInputDevice *device,
guint32 time,
float prev_x,
float prev_y,
float *x,
float *y)
{
MetaPointerConfinementWayland *self =
META_POINTER_CONFINEMENT_WAYLAND (constraint);
MetaWaylandSurface *surface;
cairo_region_t *region;
float sx, sy;
float prev_sx, prev_sy;
GArray *borders;
MetaLine2 motion;
MetaBorder *closest_border;
uint32_t directions;
surface = meta_wayland_pointer_constraint_get_surface (self->constraint);
meta_wayland_surface_get_relative_coordinates (surface, *x, *y, &sx, &sy);
meta_wayland_surface_get_relative_coordinates (surface, prev_x, prev_y,
&prev_sx, &prev_sy);
/* For motions in a positive direction on any axis, append the smallest
* possible value representable in a Wayland absolute coordinate. This is
* in order to avoid not clamping motion that as a floating point number
* won't be clamped, but will be rounded up to be outside of the range
* of wl_fixed_t. */
if (sx > prev_sx)
sx += (float)wl_fixed_to_double(1);
if (sy > prev_sy)
sy += (float)wl_fixed_to_double(1);
borders = g_array_new (FALSE, FALSE, sizeof (MetaBorder));
/*
* Generate borders given the confine region we are to use. The borders
* are defined to be the outer region of the allowed area. This means
* top/left borders are "within" the allowed area, while bottom/right
* borders are outside. This needs to be considered when clamping
* confined motion vectors.
*/
region =
meta_wayland_pointer_constraint_calculate_effective_region (self->constraint);
region_to_outline (region, borders);
cairo_region_destroy (region);
motion = (MetaLine2) {
.a = (MetaVector2) {
.x = prev_sx,
.y = prev_sy,
},
.b = (MetaVector2) {
.x = sx,
.y = sy,
},
};
directions = get_motion_directions (&motion);
while (directions)
{
closest_border = get_closest_border (borders,
&motion,
directions);
if (closest_border)
clamp_to_border (closest_border, &motion, &directions);
else
break;
}
meta_wayland_surface_get_absolute_coordinates (surface,
motion.b.x, motion.b.y,
x, y);
g_array_free (borders, FALSE);
}
static float
point_to_border_distance_2 (MetaBorder *border,
float x,
float y)
{
float orig_x, orig_y;
float dx, dy;
if (meta_border_is_horizontal (border))
{
if (x < border->line.a.x)
orig_x = border->line.a.x;
else if (x > border->line.b.x)
orig_x = border->line.b.x;
else
orig_x = x;
orig_y = border->line.a.y;
}
else
{
if (y < border->line.a.y)
orig_y = border->line.a.y;
else if (y > border->line.b.y)
orig_y = border->line.b.y;
else
orig_y = y;
orig_x = border->line.a.x;
}
dx = fabsf (orig_x - x);
dy = fabsf (orig_y - y);
return dx*dx + dy*dy;
}
static void
warp_to_behind_border (MetaBorder *border,
float *sx,
float *sy)
{
switch (border->blocking_directions)
{
case META_BORDER_MOTION_DIRECTION_POSITIVE_X:
case META_BORDER_MOTION_DIRECTION_NEGATIVE_X:
if (border->blocking_directions == META_BORDER_MOTION_DIRECTION_POSITIVE_X)
*sx = border->line.a.x - wl_fixed_to_double (1);
else
*sx = border->line.a.x + wl_fixed_to_double (1);
if (*sy < border->line.a.y)
*sy = border->line.a.y + wl_fixed_to_double (1);
else if (*sy > border->line.b.y)
*sy = border->line.b.y - wl_fixed_to_double (1);
break;
case META_BORDER_MOTION_DIRECTION_POSITIVE_Y:
case META_BORDER_MOTION_DIRECTION_NEGATIVE_Y:
if (border->blocking_directions == META_BORDER_MOTION_DIRECTION_POSITIVE_Y)
*sy = border->line.a.y - wl_fixed_to_double (1);
else
*sy = border->line.a.y + wl_fixed_to_double (1);
if (*sx < border->line.a.x)
*sx = border->line.a.x + wl_fixed_to_double (1);
else if (*sx > (border->line.b.x))
*sx = border->line.b.x - wl_fixed_to_double (1);
break;
}
}
static void
meta_pointer_confinement_wayland_maybe_warp (MetaPointerConfinementWayland *self)
{
MetaWaylandSeat *seat;
MetaWaylandSurface *surface;
ClutterPoint point;
float sx;
float sy;
cairo_region_t *region;
seat = meta_wayland_pointer_constraint_get_seat (self->constraint);
surface = meta_wayland_pointer_constraint_get_surface (self->constraint);
clutter_input_device_get_coords (seat->pointer.device, NULL, &point);
meta_wayland_surface_get_relative_coordinates (surface,
point.x, point.y,
&sx, &sy);
region =
meta_wayland_pointer_constraint_calculate_effective_region (self->constraint);
if (!cairo_region_contains_point (region, (int)sx, (int)sy))
{
GArray *borders;
float closest_distance_2 = FLT_MAX;
MetaBorder *closest_border = NULL;
unsigned int i;
float x;
float y;
borders = g_array_new (FALSE, FALSE, sizeof (MetaBorder));
region_to_outline (region, borders);
for (i = 0; i < borders->len; i++)
{
MetaBorder *border = &g_array_index (borders, MetaBorder, i);
float distance_2;
distance_2 = point_to_border_distance_2 (border, sx, sy);
if (distance_2 < closest_distance_2)
{
closest_border = border;
closest_distance_2 = distance_2;
}
}
warp_to_behind_border (closest_border, &sx, &sy);
meta_wayland_surface_get_absolute_coordinates (surface, sx, sy, &x, &y);
meta_backend_warp_pointer (meta_get_backend (), (int)x, (int)y);
}
cairo_region_destroy (region);
}
static void
surface_actor_painting (MetaSurfaceActorWayland *surface_actor,
MetaPointerConfinementWayland *self)
{
meta_pointer_confinement_wayland_maybe_warp (self);
}
MetaPointerConstraint *
meta_pointer_confinement_wayland_new (MetaWaylandPointerConstraint *constraint)
{
GObject *object;
MetaPointerConfinementWayland *confinement;
MetaWaylandSurface *surface;
object = g_object_new (META_TYPE_POINTER_CONFINEMENT_WAYLAND, NULL);
confinement = META_POINTER_CONFINEMENT_WAYLAND (object);
confinement->constraint = constraint;
surface = meta_wayland_pointer_constraint_get_surface (constraint);
g_signal_connect_object (surface->surface_actor,
"painting",
G_CALLBACK (surface_actor_painting),
confinement,
0);
return META_POINTER_CONSTRAINT (confinement);
}
static void
meta_pointer_confinement_wayland_init (MetaPointerConfinementWayland *confinement_wayland)
{
}
static void
meta_pointer_confinement_wayland_class_init (MetaPointerConfinementWaylandClass *klass)
{
MetaPointerConstraintClass *pointer_constraint_class =
META_POINTER_CONSTRAINT_CLASS (klass);
pointer_constraint_class->constrain = meta_pointer_confinement_wayland_constrain;
}

View File

@@ -0,0 +1,45 @@
/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
/*
* Copyright (C) 2015 Red Hat
*
* 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.
*
* Written by:
* Jonas Ådahl <jadahl@gmail.com>
*/
#ifndef META_POINTER_CONFINEMENT_WAYLAND_H
#define META_POINTER_CONFINEMENT_WAYLAND_H
#include <glib-object.h>
#include "backends/meta-pointer-constraint.h"
#include "wayland/meta-wayland-pointer-constraints.h"
G_BEGIN_DECLS
#define META_TYPE_POINTER_CONFINEMENT_WAYLAND (meta_pointer_confinement_wayland_get_type ())
G_DECLARE_FINAL_TYPE (MetaPointerConfinementWayland,
meta_pointer_confinement_wayland,
META, POINTER_CONFINEMENT_WAYLAND,
MetaPointerConstraint);
MetaPointerConstraint *meta_pointer_confinement_wayland_new (MetaWaylandPointerConstraint *constraint);
G_END_DECLS
#endif /* META_CONFINEMENT_WAYLAND_H */

View File

@@ -0,0 +1,72 @@
/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
/*
* Copyright (C) 2015 Red Hat
*
* 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.
*
* Written by:
* Jonas Ådahl <jadahl@gmail.com>
*/
#include "config.h"
#include "wayland/meta-pointer-lock-wayland.h"
#include <glib-object.h>
#include "backends/meta-pointer-constraint.h"
struct _MetaPointerLockWayland
{
MetaPointerConstraint parent;
};
G_DEFINE_TYPE (MetaPointerLockWayland, meta_pointer_lock_wayland,
META_TYPE_POINTER_CONSTRAINT);
static void
meta_pointer_lock_wayland_constrain (MetaPointerConstraint *constraint,
ClutterInputDevice *device,
guint32 time,
float prev_x,
float prev_y,
float *x,
float *y)
{
*x = prev_x;
*y = prev_y;
}
MetaPointerConstraint *
meta_pointer_lock_wayland_new (void)
{
return g_object_new (META_TYPE_POINTER_LOCK_WAYLAND, NULL);
}
static void
meta_pointer_lock_wayland_init (MetaPointerLockWayland *lock_wayland)
{
}
static void
meta_pointer_lock_wayland_class_init (MetaPointerLockWaylandClass *klass)
{
MetaPointerConstraintClass *pointer_constraint_class =
META_POINTER_CONSTRAINT_CLASS (klass);
pointer_constraint_class->constrain = meta_pointer_lock_wayland_constrain;
}

View File

@@ -0,0 +1,42 @@
/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
/*
* Copyright (C) 2015 Red Hat
*
* 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.
*
* Written by:
* Jonas Ådahl <jadahl@gmail.com>
*/
#ifndef META_POINTER_LOCK_WAYLAND_H
#define META_POINTER_LOCK_WAYLAND_H
#include <glib-object.h>
#include "backends/meta-pointer-constraint.h"
G_BEGIN_DECLS
#define META_TYPE_POINTER_LOCK_WAYLAND (meta_pointer_lock_wayland_get_type ())
G_DECLARE_FINAL_TYPE (MetaPointerLockWayland, meta_pointer_lock_wayland,
META, POINTER_LOCK_WAYLAND, MetaPointerConstraint);
MetaPointerConstraint *meta_pointer_lock_wayland_new (void);
G_END_DECLS
#endif /* META_LOCK_WAYLAND_H */

View File

@@ -54,10 +54,31 @@ meta_wayland_buffer_unref (MetaWaylandBuffer *buffer)
if (buffer->ref_count == 0)
{
g_clear_pointer (&buffer->texture, cogl_object_unref);
wl_resource_queue_event (buffer->resource, WL_BUFFER_RELEASE);
if (buffer->accessible)
meta_wayland_buffer_release_control (buffer);
}
}
void
meta_wayland_buffer_take_control (MetaWaylandBuffer *buffer)
{
if (buffer->accessible)
meta_fatal ("buffer control taken twice");
buffer->accessible = TRUE;
}
void
meta_wayland_buffer_release_control (MetaWaylandBuffer *buffer)
{
if (!buffer->accessible)
meta_fatal ("buffer released when not in control");
wl_resource_queue_event (buffer->resource, WL_BUFFER_RELEASE);
buffer->accessible = FALSE;
}
MetaWaylandBuffer *
meta_wayland_buffer_from_resource (struct wl_resource *resource)
{
@@ -93,6 +114,9 @@ meta_wayland_buffer_ensure_texture (MetaWaylandBuffer *buffer)
CoglTexture *texture;
struct wl_shm_buffer *shm_buffer;
if (!buffer->accessible)
meta_warning ("attempted to process damage on uncommitted buffer");
if (buffer->texture)
goto out;
@@ -116,6 +140,9 @@ meta_wayland_buffer_ensure_texture (MetaWaylandBuffer *buffer)
buffer->texture = texture;
if (shm_buffer)
buffer->copied_data = TRUE;
out:
return buffer->texture;
}
@@ -126,6 +153,9 @@ meta_wayland_buffer_process_damage (MetaWaylandBuffer *buffer,
{
struct wl_shm_buffer *shm_buffer;
if (!buffer->accessible)
meta_warning ("attempted to process damage on uncommitted buffer");
shm_buffer = wl_shm_buffer_get (buffer->resource);
if (shm_buffer)

View File

@@ -39,11 +39,16 @@ struct _MetaWaylandBuffer
CoglTexture *texture;
uint32_t ref_count;
uint32_t accessible : 1;
uint32_t copied_data : 1;
};
MetaWaylandBuffer * meta_wayland_buffer_from_resource (struct wl_resource *resource);
void meta_wayland_buffer_ref (MetaWaylandBuffer *buffer);
void meta_wayland_buffer_unref (MetaWaylandBuffer *buffer);
void meta_wayland_buffer_take_control (MetaWaylandBuffer *buffer);
void meta_wayland_buffer_release_control (MetaWaylandBuffer *buffer);
CoglTexture * meta_wayland_buffer_ensure_texture (MetaWaylandBuffer *buffer);
void meta_wayland_buffer_process_damage (MetaWaylandBuffer *buffer,
cairo_region_t *region);

View File

@@ -37,17 +37,30 @@
#include "meta-wayland-private.h"
#include "meta-dnd-actor-private.h"
#define ALL_ACTIONS (WL_DATA_DEVICE_MANAGER_DND_ACTION_COPY | \
WL_DATA_DEVICE_MANAGER_DND_ACTION_MOVE | \
WL_DATA_DEVICE_MANAGER_DND_ACTION_ASK)
struct _MetaWaylandDataOffer
{
struct wl_resource *resource;
MetaWaylandDataSource *source;
struct wl_listener source_destroy_listener;
uint32_t dnd_actions;
enum wl_data_device_manager_dnd_action preferred_dnd_action;
};
typedef struct _MetaWaylandDataSourcePrivate
{
MetaWaylandDataOffer *offer;
struct wl_array mime_types;
gboolean has_target;
uint32_t dnd_actions;
enum wl_data_device_manager_dnd_action user_dnd_action;
enum wl_data_device_manager_dnd_action current_dnd_action;
MetaWaylandSeat *seat;
guint actions_set : 1;
guint in_ask : 1;
} MetaWaylandDataSourcePrivate;
typedef struct _MetaWaylandDataSourceWayland
@@ -74,6 +87,75 @@ unbind_resource (struct wl_resource *resource)
wl_list_remove (wl_resource_get_link (resource));
}
static gboolean
meta_wayland_source_get_in_ask (MetaWaylandDataSource *source)
{
MetaWaylandDataSourcePrivate *priv =
meta_wayland_data_source_get_instance_private (source);
return priv->in_ask;
}
static void
meta_wayland_source_update_in_ask (MetaWaylandDataSource *source)
{
MetaWaylandDataSourcePrivate *priv =
meta_wayland_data_source_get_instance_private (source);
priv->in_ask =
priv->current_dnd_action == WL_DATA_DEVICE_MANAGER_DND_ACTION_ASK;
}
static enum wl_data_device_manager_dnd_action
data_offer_choose_action (MetaWaylandDataOffer *offer)
{
MetaWaylandDataSource *source = offer->source;
uint32_t actions, user_action, available_actions;
actions = meta_wayland_data_source_get_actions (source);
user_action = meta_wayland_data_source_get_user_action (source);
available_actions = actions & offer->dnd_actions;
if (!available_actions)
return WL_DATA_DEVICE_MANAGER_DND_ACTION_NONE;
/* If the user is forcing an action, go for it */
if ((user_action & available_actions) != 0)
return user_action;
/* If the dest side has a preferred DnD action, use it */
if ((offer->preferred_dnd_action & available_actions) != 0)
return offer->preferred_dnd_action;
/* Use the first found action, in bit order */
return 1 << (ffs (available_actions) - 1);
}
static void
data_offer_update_action (MetaWaylandDataOffer *offer)
{
enum wl_data_device_manager_dnd_action current_action, action;
MetaWaylandDataSource *source;
if (!offer->source)
return;
source = offer->source;
current_action = meta_wayland_data_source_get_current_action (source);
action = data_offer_choose_action (offer);
if (current_action == action)
return;
meta_wayland_data_source_set_current_action (source, action);
if (!meta_wayland_source_get_in_ask (source) &&
wl_resource_get_version (offer->resource) >=
WL_DATA_OFFER_ACTION_SINCE_VERSION)
wl_data_offer_send_action (offer->resource, action);
}
static void
meta_wayland_data_source_target (MetaWaylandDataSource *source,
const char *mime_type)
@@ -98,6 +180,25 @@ meta_wayland_data_source_has_target (MetaWaylandDataSource *source)
return priv->has_target;
}
static void
meta_wayland_data_source_set_seat (MetaWaylandDataSource *source,
MetaWaylandSeat *seat)
{
MetaWaylandDataSourcePrivate *priv =
meta_wayland_data_source_get_instance_private (source);
priv->seat = seat;
}
static MetaWaylandSeat *
meta_wayland_data_source_get_seat (MetaWaylandDataSource *source)
{
MetaWaylandDataSourcePrivate *priv =
meta_wayland_data_source_get_instance_private (source);
return priv->seat;
}
void
meta_wayland_data_source_set_has_target (MetaWaylandDataSource *source,
gboolean has_target)
@@ -112,7 +213,7 @@ struct wl_array *
meta_wayland_data_source_get_mime_types (const MetaWaylandDataSource *source)
{
MetaWaylandDataSourcePrivate *priv =
meta_wayland_data_source_get_instance_private (source);
meta_wayland_data_source_get_instance_private ((MetaWaylandDataSource *)source);
return &priv->mime_types;
}
@@ -123,6 +224,100 @@ meta_wayland_data_source_cancel (MetaWaylandDataSource *source)
META_WAYLAND_DATA_SOURCE_GET_CLASS (source)->cancel (source);
}
uint32_t
meta_wayland_data_source_get_actions (MetaWaylandDataSource *source)
{
MetaWaylandDataSourcePrivate *priv =
meta_wayland_data_source_get_instance_private (source);
return priv->dnd_actions;
}
enum wl_data_device_manager_dnd_action
meta_wayland_data_source_get_user_action (MetaWaylandDataSource *source)
{
MetaWaylandDataSourcePrivate *priv =
meta_wayland_data_source_get_instance_private (source);
if (!priv->seat)
return WL_DATA_DEVICE_MANAGER_DND_ACTION_NONE;
return priv->user_dnd_action;
}
enum wl_data_device_manager_dnd_action
meta_wayland_data_source_get_current_action (MetaWaylandDataSource *source)
{
MetaWaylandDataSourcePrivate *priv =
meta_wayland_data_source_get_instance_private (source);
return priv->current_dnd_action;
}
static void
meta_wayland_data_source_set_current_offer (MetaWaylandDataSource *source,
MetaWaylandDataOffer *offer)
{
MetaWaylandDataSourcePrivate *priv =
meta_wayland_data_source_get_instance_private (source);
priv->offer = offer;
}
static MetaWaylandDataOffer *
meta_wayland_data_source_get_current_offer (MetaWaylandDataSource *source)
{
MetaWaylandDataSourcePrivate *priv =
meta_wayland_data_source_get_instance_private (source);
return priv->offer;
}
void
meta_wayland_data_source_set_current_action (MetaWaylandDataSource *source,
enum wl_data_device_manager_dnd_action action)
{
MetaWaylandDataSourcePrivate *priv =
meta_wayland_data_source_get_instance_private (source);
if (priv->current_dnd_action == action)
return;
priv->current_dnd_action = action;
if (!meta_wayland_source_get_in_ask (source))
META_WAYLAND_DATA_SOURCE_GET_CLASS (source)->action (source, action);
}
void
meta_wayland_data_source_set_actions (MetaWaylandDataSource *source,
uint32_t dnd_actions)
{
MetaWaylandDataSourcePrivate *priv =
meta_wayland_data_source_get_instance_private (source);
priv->dnd_actions = dnd_actions;
priv->actions_set = TRUE;
}
static void
meta_wayland_data_source_set_user_action (MetaWaylandDataSource *source,
enum wl_data_device_manager_dnd_action action)
{
MetaWaylandDataSourcePrivate *priv =
meta_wayland_data_source_get_instance_private (source);
MetaWaylandDataOffer *offer;
if (priv->user_dnd_action == action)
return;
priv->user_dnd_action = action;
offer = meta_wayland_data_source_get_current_offer (source);
if (offer)
data_offer_update_action (offer);
}
static void
data_offer_accept (struct wl_client *client,
struct wl_resource *resource,
@@ -161,21 +356,117 @@ data_offer_destroy (struct wl_client *client, struct wl_resource *resource)
wl_resource_destroy (resource);
}
static void
data_offer_finish (struct wl_client *client,
struct wl_resource *resource)
{
MetaWaylandDataOffer *offer = wl_resource_get_user_data (resource);
enum wl_data_device_manager_dnd_action current_action;
if (!offer->source ||
offer != meta_wayland_data_source_get_current_offer (offer->source))
return;
if (meta_wayland_data_source_get_seat (offer->source) ||
!meta_wayland_data_source_has_target (offer->source))
{
wl_resource_post_error (offer->resource,
WL_DATA_OFFER_ERROR_INVALID_FINISH,
"premature finish request");
return;
}
current_action = meta_wayland_data_source_get_current_action (offer->source);
if (current_action == WL_DATA_DEVICE_MANAGER_DND_ACTION_NONE ||
current_action == WL_DATA_DEVICE_MANAGER_DND_ACTION_ASK)
{
wl_resource_post_error (offer->resource,
WL_DATA_OFFER_ERROR_INVALID_OFFER,
"offer finished with an invalid action");
return;
}
meta_wayland_data_source_notify_finish (offer->source);
}
static void
data_offer_set_actions (struct wl_client *client,
struct wl_resource *resource,
uint32_t dnd_actions,
uint32_t preferred_action)
{
MetaWaylandDataOffer *offer = wl_resource_get_user_data (resource);
if (dnd_actions & ~(ALL_ACTIONS))
{
wl_resource_post_error (offer->resource,
WL_DATA_OFFER_ERROR_INVALID_ACTION_MASK,
"invalid actions mask %x", dnd_actions);
return;
}
if (preferred_action &&
(!(preferred_action & dnd_actions) ||
__builtin_popcount (preferred_action) > 1))
{
wl_resource_post_error (offer->resource,
WL_DATA_OFFER_ERROR_INVALID_ACTION,
"invalid action %x", preferred_action);
return;
}
offer->dnd_actions = dnd_actions;
offer->preferred_dnd_action = preferred_action;
data_offer_update_action (offer);
}
static const struct wl_data_offer_interface data_offer_interface = {
data_offer_accept,
data_offer_receive,
data_offer_destroy,
data_offer_finish,
data_offer_set_actions,
};
static void
meta_wayland_data_source_notify_drop_performed (MetaWaylandDataSource *source)
{
META_WAYLAND_DATA_SOURCE_GET_CLASS (source)->drop_performed (source);
}
void
meta_wayland_data_source_notify_finish (MetaWaylandDataSource *source)
{
META_WAYLAND_DATA_SOURCE_GET_CLASS (source)->drag_finished (source);
}
static void
destroy_data_offer (struct wl_resource *resource)
{
MetaWaylandDataOffer *offer = wl_resource_get_user_data (resource);
if (offer->source)
g_object_remove_weak_pointer (G_OBJECT (offer->source),
(gpointer *)&offer->source);
{
if (offer == meta_wayland_data_source_get_current_offer (offer->source))
{
if (wl_resource_get_version (offer->resource) <
WL_DATA_OFFER_ACTION_SINCE_VERSION)
meta_wayland_data_source_notify_finish (offer->source);
else
{
meta_wayland_data_source_cancel (offer->source);
meta_wayland_data_source_set_current_offer (offer->source, NULL);
}
}
g_object_remove_weak_pointer (G_OBJECT (offer->source),
(gpointer *)&offer->source);
offer->source = NULL;
}
meta_display_sync_wayland_input_focus (meta_get_display ());
g_slice_free (MetaWaylandDataOffer, offer);
}
@@ -203,6 +494,9 @@ meta_wayland_data_source_send_offer (MetaWaylandDataSource *source,
wl_array_for_each (p, &priv->mime_types)
wl_data_offer_send_offer (offer->resource, *p);
data_offer_update_action (offer);
meta_wayland_data_source_set_current_offer (source, offer);
return offer->resource;
}
@@ -222,14 +516,56 @@ data_source_destroy (struct wl_client *client, struct wl_resource *resource)
wl_resource_destroy (resource);
}
static void
data_source_set_actions (struct wl_client *client,
struct wl_resource *resource,
uint32_t dnd_actions)
{
MetaWaylandDataSource *source = wl_resource_get_user_data (resource);
MetaWaylandDataSourcePrivate *priv =
meta_wayland_data_source_get_instance_private (source);
MetaWaylandDataSourceWayland *source_wayland =
META_WAYLAND_DATA_SOURCE_WAYLAND (source);
if (priv->actions_set)
{
wl_resource_post_error (source_wayland->resource,
WL_DATA_SOURCE_ERROR_INVALID_ACTION_MASK,
"cannot set actions more than once");
return;
}
if (dnd_actions & ~(ALL_ACTIONS))
{
wl_resource_post_error (source_wayland->resource,
WL_DATA_SOURCE_ERROR_INVALID_ACTION_MASK,
"invalid actions mask %x", dnd_actions);
return;
}
if (meta_wayland_data_source_get_seat (source))
{
wl_resource_post_error (source_wayland->resource,
WL_DATA_SOURCE_ERROR_INVALID_ACTION_MASK,
"invalid action change after "
"wl_data_device.start_drag");
return;
}
meta_wayland_data_source_set_actions (source, dnd_actions);
}
static struct wl_data_source_interface data_source_interface = {
data_source_offer,
data_source_destroy
data_source_destroy,
data_source_set_actions
};
struct _MetaWaylandDragGrab {
MetaWaylandPointerGrab generic;
MetaWaylandKeyboardGrab keyboard_grab;
MetaWaylandSeat *seat;
struct wl_client *drag_client;
@@ -248,6 +584,7 @@ struct _MetaWaylandDragGrab {
struct wl_listener drag_origin_listener;
int drag_start_x, drag_start_y;
ClutterModifierType buttons;
};
static void
@@ -256,6 +593,24 @@ destroy_drag_focus (struct wl_listener *listener, void *data)
MetaWaylandDragGrab *grab = wl_container_of (listener, grab, drag_focus_listener);
grab->drag_focus_data_device = NULL;
grab->drag_focus = NULL;
}
static void
meta_wayland_drag_grab_set_source (MetaWaylandDragGrab *drag_grab,
MetaWaylandDataSource *source)
{
if (drag_grab->drag_data_source)
g_object_weak_unref (G_OBJECT (drag_grab->drag_data_source),
drag_grab_data_source_destroyed,
drag_grab);
drag_grab->drag_data_source = source;
if (source)
g_object_weak_ref (G_OBJECT (source),
drag_grab_data_source_destroyed,
drag_grab);
}
void
@@ -275,6 +630,9 @@ meta_wayland_drag_grab_set_focus (MetaWaylandDragGrab *drag_grab,
drag_grab->drag_focus = NULL;
}
if (drag_grab->drag_data_source)
meta_wayland_data_source_set_current_offer (drag_grab->drag_data_source, NULL);
if (!surface)
return;
@@ -312,6 +670,22 @@ drag_grab_focus (MetaWaylandPointerGrab *grab,
meta_wayland_drag_grab_set_focus (drag_grab, surface);
}
static void
data_source_update_user_dnd_action (MetaWaylandDataSource *source,
ClutterModifierType modifiers)
{
enum wl_data_device_manager_dnd_action user_dnd_action = 0;
if (modifiers & CLUTTER_SHIFT_MASK)
user_dnd_action = WL_DATA_DEVICE_MANAGER_DND_ACTION_MOVE;
else if (modifiers & CLUTTER_CONTROL_MASK)
user_dnd_action = WL_DATA_DEVICE_MANAGER_DND_ACTION_COPY;
else if (modifiers & (CLUTTER_MOD1_MASK | CLUTTER_BUTTON2_MASK))
user_dnd_action = WL_DATA_DEVICE_MANAGER_DND_ACTION_ASK;
meta_wayland_data_source_set_user_action (source, user_dnd_action);
}
static void
drag_grab_motion (MetaWaylandPointerGrab *grab,
const ClutterEvent *event)
@@ -343,10 +717,7 @@ data_device_end_drag_grab (MetaWaylandDragGrab *drag_grab)
wl_list_remove (&drag_grab->drag_icon_listener.link);
}
if (drag_grab->drag_data_source)
g_object_weak_unref (G_OBJECT (drag_grab->drag_data_source),
drag_grab_data_source_destroyed,
drag_grab);
meta_wayland_drag_grab_set_source (drag_grab, NULL);
if (drag_grab->feedback_actor)
{
@@ -356,7 +727,15 @@ data_device_end_drag_grab (MetaWaylandDragGrab *drag_grab)
drag_grab->seat->data_device.current_grab = NULL;
meta_wayland_pointer_end_grab (drag_grab->generic.pointer);
/* There might be other grabs created in result to DnD actions like popups
* on "ask" actions, we must not reset those, only our own.
*/
if (drag_grab->generic.pointer->grab == (MetaWaylandPointerGrab *) drag_grab)
{
meta_wayland_pointer_end_grab (drag_grab->generic.pointer);
meta_wayland_keyboard_end_grab (drag_grab->keyboard_grab.keyboard);
}
g_slice_free (MetaWaylandDragGrab, drag_grab);
}
@@ -371,17 +750,33 @@ drag_grab_button (MetaWaylandPointerGrab *grab,
if (drag_grab->generic.pointer->grab_button == clutter_event_get_button (event) &&
event_type == CLUTTER_BUTTON_RELEASE)
{
gboolean success = FALSE;
MetaWaylandDataSource *source = drag_grab->drag_data_source;
gboolean success;
if (meta_wayland_data_source_has_target (drag_grab->drag_data_source))
if (drag_grab->drag_focus && source &&
meta_wayland_data_source_has_target (source) &&
meta_wayland_data_source_get_current_action (source))
{
/* Detach the data source from the grab, it's meant to live longer */
meta_wayland_drag_grab_set_source (drag_grab, NULL);
meta_wayland_data_source_set_seat (source, NULL);
meta_wayland_surface_drag_dest_drop (drag_grab->drag_focus);
meta_wayland_data_source_notify_drop_performed (source);
meta_wayland_source_update_in_ask (source);
success = TRUE;
}
else
{
meta_wayland_data_source_cancel (source);
meta_wayland_data_source_set_current_offer (source, NULL);
meta_wayland_data_device_set_dnd_source (&seat->data_device, NULL);
success= FALSE;
}
/* Finish drag and let actor self-destruct */
meta_dnd_actor_drag_finish (META_DND_ACTOR (drag_grab->feedback_actor),
success);
meta_dnd_actor_drag_finish (META_DND_ACTOR (drag_grab->feedback_actor), success);
drag_grab->feedback_actor = NULL;
}
@@ -396,6 +791,40 @@ static const MetaWaylandPointerGrabInterface drag_grab_interface = {
drag_grab_button,
};
static gboolean
keyboard_drag_grab_key (MetaWaylandKeyboardGrab *grab,
const ClutterEvent *event)
{
return FALSE;
}
static void
keyboard_drag_grab_modifiers (MetaWaylandKeyboardGrab *grab,
ClutterModifierType modifiers)
{
MetaWaylandDragGrab *drag_grab;
drag_grab = wl_container_of (grab, drag_grab, keyboard_grab);
/* The modifiers here just contain keyboard modifiers, mix it with the
* mouse button modifiers we got when starting the drag operation.
*/
modifiers |= drag_grab->buttons;
if (drag_grab->drag_data_source)
{
data_source_update_user_dnd_action (drag_grab->drag_data_source, modifiers);
if (drag_grab->drag_focus)
meta_wayland_surface_drag_dest_update (drag_grab->drag_focus);
}
}
static const MetaWaylandKeyboardGrabInterface keyboard_drag_grab_interface = {
keyboard_drag_grab_key,
keyboard_drag_grab_modifiers
};
static void
destroy_data_device_origin (struct wl_listener *listener, void *data)
{
@@ -440,12 +869,16 @@ meta_wayland_data_device_start_drag (MetaWaylandDataDevice *data
MetaWaylandSeat *seat = wl_container_of (data_device, seat, data_device);
MetaWaylandDragGrab *drag_grab;
ClutterPoint pos, stage_pos;
ClutterModifierType modifiers;
data_device->current_grab = drag_grab = g_slice_new0 (MetaWaylandDragGrab);
drag_grab->generic.interface = funcs;
drag_grab->generic.pointer = &seat->pointer;
drag_grab->keyboard_grab.interface = &keyboard_drag_grab_interface;
drag_grab->keyboard_grab.keyboard = &seat->keyboard;
drag_grab->drag_client = client;
drag_grab->seat = seat;
@@ -460,13 +893,15 @@ meta_wayland_data_device_start_drag (MetaWaylandDataDevice *data
drag_grab->drag_start_x = stage_pos.x;
drag_grab->drag_start_y = stage_pos.y;
g_object_weak_ref (G_OBJECT (source),
drag_grab_data_source_destroyed,
drag_grab);
modifiers = clutter_input_device_get_modifier_state (seat->pointer.device);
drag_grab->buttons = modifiers &
(CLUTTER_BUTTON1_MASK | CLUTTER_BUTTON2_MASK | CLUTTER_BUTTON3_MASK |
CLUTTER_BUTTON4_MASK | CLUTTER_BUTTON5_MASK);
drag_grab->drag_data_source = source;
meta_wayland_drag_grab_set_source (drag_grab, source);
meta_wayland_data_device_set_dnd_source (data_device,
drag_grab->drag_data_source);
data_source_update_user_dnd_action (source, modifiers);
if (icon_surface)
{
@@ -480,8 +915,7 @@ meta_wayland_data_device_start_drag (MetaWaylandDataDevice *data
drag_grab->drag_start_x,
drag_grab->drag_start_y);
meta_feedback_actor_set_anchor (META_FEEDBACK_ACTOR (drag_grab->feedback_actor),
-drag_grab->drag_surface->offset_x,
-drag_grab->drag_surface->offset_y);
0, 0);
clutter_actor_add_child (drag_grab->feedback_actor,
CLUTTER_ACTOR (drag_grab->drag_surface->surface_actor));
@@ -490,6 +924,7 @@ meta_wayland_data_device_start_drag (MetaWaylandDataDevice *data
}
meta_wayland_pointer_start_grab (&seat->pointer, (MetaWaylandPointerGrab*) drag_grab);
meta_wayland_data_source_set_seat (source, seat);
}
void
@@ -548,6 +983,10 @@ data_device_start_drag (struct wl_client *client,
meta_wayland_data_device_start_drag (data_device, client,
&drag_grab_interface,
surface, drag_source, icon_surface);
meta_wayland_keyboard_set_focus (&seat->keyboard, NULL);
meta_wayland_keyboard_start_grab (&seat->keyboard,
&seat->data_device.current_grab->keyboard_grab);
}
static void
@@ -600,6 +1039,47 @@ meta_wayland_source_cancel (MetaWaylandDataSource *source)
wl_data_source_send_cancelled (source_wayland->resource);
}
static void
meta_wayland_source_action (MetaWaylandDataSource *source,
enum wl_data_device_manager_dnd_action action)
{
MetaWaylandDataSourceWayland *source_wayland =
META_WAYLAND_DATA_SOURCE_WAYLAND (source);
if (wl_resource_get_version (source_wayland->resource) >=
WL_DATA_SOURCE_ACTION_SINCE_VERSION)
wl_data_source_send_action (source_wayland->resource, action);
}
static void
meta_wayland_source_drop_performed (MetaWaylandDataSource *source)
{
MetaWaylandDataSourceWayland *source_wayland =
META_WAYLAND_DATA_SOURCE_WAYLAND (source);
if (wl_resource_get_version (source_wayland->resource) >=
WL_DATA_SOURCE_DND_DROP_PERFORMED_SINCE_VERSION)
wl_data_source_send_dnd_drop_performed (source_wayland->resource);
}
static void
meta_wayland_source_drag_finished (MetaWaylandDataSource *source)
{
MetaWaylandDataSourceWayland *source_wayland =
META_WAYLAND_DATA_SOURCE_WAYLAND (source);
enum wl_data_device_manager_dnd_action action;
if (meta_wayland_source_get_in_ask (source))
{
action = meta_wayland_data_source_get_current_action (source);
meta_wayland_source_action (source, action);
}
if (wl_resource_get_version (source_wayland->resource) >=
WL_DATA_SOURCE_DND_FINISHED_SINCE_VERSION)
wl_data_source_send_dnd_finished (source_wayland->resource);
}
static void
meta_wayland_source_finalize (GObject *object)
{
@@ -623,6 +1103,9 @@ meta_wayland_data_source_wayland_class_init (MetaWaylandDataSourceWaylandClass *
data_source_class->send = meta_wayland_source_send;
data_source_class->target = meta_wayland_source_target;
data_source_class->cancel = meta_wayland_source_cancel;
data_source_class->action = meta_wayland_source_action;
data_source_class->drop_performed = meta_wayland_source_drop_performed;
data_source_class->drag_finished = meta_wayland_source_drag_finished;
}
static void
@@ -647,6 +1130,7 @@ meta_wayland_data_source_init (MetaWaylandDataSource *source)
meta_wayland_data_source_get_instance_private (source);
wl_array_init (&priv->mime_types);
priv->current_dnd_action = -1;
}
static void
@@ -665,6 +1149,7 @@ meta_wayland_drag_dest_focus_in (MetaWaylandDataDevice *data_device,
MetaWaylandDragGrab *grab = data_device->current_grab;
struct wl_display *display;
struct wl_client *client;
uint32_t source_actions;
wl_fixed_t sx, sy;
if (!grab->drag_focus_data_device)
@@ -677,6 +1162,13 @@ meta_wayland_drag_dest_focus_in (MetaWaylandDataDevice *data_device,
wl_resource_add_destroy_listener (grab->drag_focus_data_device,
&grab->drag_focus_listener);
if (wl_resource_get_version (offer->resource) >=
WL_DATA_OFFER_SOURCE_ACTIONS_SINCE_VERSION)
{
source_actions = meta_wayland_data_source_get_actions (offer->source);
wl_data_offer_send_source_actions (offer->resource, source_actions);
}
meta_wayland_pointer_get_relative_coordinates (grab->generic.pointer,
surface, &sx, &sy);
wl_data_device_send_enter (grab->drag_focus_data_device,
@@ -722,11 +1214,18 @@ meta_wayland_drag_dest_drop (MetaWaylandDataDevice *data_device,
wl_data_device_send_drop (grab->drag_focus_data_device);
}
static void
meta_wayland_drag_dest_update (MetaWaylandDataDevice *data_device,
MetaWaylandSurface *surface)
{
}
static const MetaWaylandDragDestFuncs meta_wayland_drag_dest_funcs = {
meta_wayland_drag_dest_focus_in,
meta_wayland_drag_dest_focus_out,
meta_wayland_drag_dest_motion,
meta_wayland_drag_dest_drop
meta_wayland_drag_dest_drop,
meta_wayland_drag_dest_update
};
const MetaWaylandDragDestFuncs *
@@ -815,6 +1314,7 @@ data_device_set_selection (struct wl_client *client,
guint32 serial)
{
MetaWaylandDataDevice *data_device = wl_resource_get_user_data (resource);
MetaWaylandDataSourcePrivate *priv;
MetaWaylandDataSource *source;
if (source_resource)
@@ -822,6 +1322,19 @@ data_device_set_selection (struct wl_client *client,
else
source = NULL;
if (source)
{
priv = meta_wayland_data_source_get_instance_private (source);
if (priv->actions_set)
{
wl_resource_post_error(source_resource,
WL_DATA_SOURCE_ERROR_INVALID_SOURCE,
"cannot set drag-and-drop source as selection");
return;
}
}
/* FIXME: Store serial and check against incoming serial here. */
meta_wayland_data_device_set_selection (data_device, source, serial);
}
@@ -943,30 +1456,12 @@ meta_wayland_data_device_is_dnd_surface (MetaWaylandDataDevice *data_device,
data_device->current_grab->drag_surface == surface;
}
void
meta_wayland_data_device_update_dnd_surface (MetaWaylandDataDevice *data_device)
{
MetaWaylandDragGrab *drag_grab;
if (!data_device->current_grab)
return;
drag_grab = data_device->current_grab;
if (!drag_grab->feedback_actor || !drag_grab->drag_surface)
return;
meta_feedback_actor_set_anchor (META_FEEDBACK_ACTOR (drag_grab->feedback_actor),
-drag_grab->drag_surface->offset_x,
-drag_grab->drag_surface->offset_y);
}
gboolean
meta_wayland_data_source_has_mime_type (const MetaWaylandDataSource *source,
const gchar *mime_type)
{
MetaWaylandDataSourcePrivate *priv =
meta_wayland_data_source_get_instance_private (source);
meta_wayland_data_source_get_instance_private ((MetaWaylandDataSource *)source);
gchar **p;
wl_array_for_each (p, &priv->mime_types)

View File

@@ -45,6 +45,11 @@ struct _MetaWaylandDataSourceClass
void (* target) (MetaWaylandDataSource *source,
const gchar *mime_type);
void (* cancel) (MetaWaylandDataSource *source);
void (* action) (MetaWaylandDataSource *source,
uint32_t action);
void (* drop_performed) (MetaWaylandDataSource *source);
void (* drag_finished) (MetaWaylandDataSource *source);
};
struct _MetaWaylandDataDevice
@@ -69,7 +74,6 @@ void meta_wayland_data_device_set_keyboard_focus (MetaWaylandDataDevice *data_de
gboolean meta_wayland_data_device_is_dnd_surface (MetaWaylandDataDevice *data_device,
MetaWaylandSurface *surface);
void meta_wayland_data_device_update_dnd_surface (MetaWaylandDataDevice *data_device);
void meta_wayland_data_device_set_dnd_source (MetaWaylandDataDevice *data_device,
MetaWaylandDataSource *source);
@@ -95,6 +99,17 @@ void meta_wayland_data_source_send (MetaWaylandDataSource *source,
const gchar *mime_type,
gint fd);
void meta_wayland_data_source_notify_finish (MetaWaylandDataSource *source);
uint32_t meta_wayland_data_source_get_actions (MetaWaylandDataSource *source);
uint32_t meta_wayland_data_source_get_user_action (MetaWaylandDataSource *source);
uint32_t meta_wayland_data_source_get_current_action (MetaWaylandDataSource *source);
void meta_wayland_data_source_set_actions (MetaWaylandDataSource *source,
uint32_t dnd_actions);
void meta_wayland_data_source_set_current_action (MetaWaylandDataSource *source,
uint32_t action);
const MetaWaylandDragDestFuncs *
meta_wayland_data_device_get_drag_dest_funcs (void);

View File

@@ -68,6 +68,7 @@
static void meta_wayland_keyboard_update_xkb_state (MetaWaylandKeyboard *keyboard);
static void notify_modifiers (MetaWaylandKeyboard *keyboard);
static guint evdev_code (const ClutterKeyEvent *event);
static void
unbind_resource (struct wl_resource *resource)
@@ -245,8 +246,10 @@ keyboard_handle_focus_surface_destroy (struct wl_listener *listener, void *data)
}
static gboolean
notify_key (MetaWaylandKeyboard *keyboard,
uint32_t time, uint32_t key, uint32_t state)
meta_wayland_keyboard_broadcast_key (MetaWaylandKeyboard *keyboard,
uint32_t time,
uint32_t key,
uint32_t state)
{
struct wl_resource *resource;
struct wl_list *l;
@@ -269,8 +272,15 @@ notify_key (MetaWaylandKeyboard *keyboard,
return (keyboard->focus_surface != NULL);
}
static gboolean
notify_key (MetaWaylandKeyboard *keyboard,
const ClutterEvent *event)
{
return keyboard->grab->interface->key (keyboard->grab, event);
}
static void
notify_modifiers (MetaWaylandKeyboard *keyboard)
meta_wayland_keyboard_broadcast_modifiers (MetaWaylandKeyboard *keyboard)
{
struct xkb_state *state;
struct wl_resource *resource;
@@ -295,6 +305,16 @@ notify_modifiers (MetaWaylandKeyboard *keyboard)
}
}
static void
notify_modifiers (MetaWaylandKeyboard *keyboard)
{
struct xkb_state *state;
state = keyboard->xkb_info.state;
keyboard->grab->interface->modifiers (keyboard->grab,
xkb_state_serialize_mods (state, XKB_STATE_MODS_EFFECTIVE));
}
static void
meta_wayland_keyboard_update_xkb_state (MetaWaylandKeyboard *keyboard)
{
@@ -374,6 +394,45 @@ settings_changed (GSettings *settings,
notify_key_repeat (keyboard);
}
static gboolean
default_grab_key (MetaWaylandKeyboardGrab *grab,
const ClutterEvent *event)
{
MetaWaylandKeyboard *keyboard = grab->keyboard;
gboolean is_press = event->type == CLUTTER_KEY_PRESS;
guint32 code;
#ifdef HAVE_NATIVE_BACKEND
MetaBackend *backend = meta_get_backend ();
#endif
/* Synthetic key events are for autorepeat. Ignore those, as
* autorepeat in Wayland is done on the client side. */
if (event->key.flags & CLUTTER_EVENT_FLAG_SYNTHETIC)
return FALSE;
#ifdef HAVE_NATIVE_BACKEND
if (META_IS_BACKEND_NATIVE (backend))
code = clutter_evdev_event_get_event_code (event);
else
#endif
code = evdev_code (&event->key);
return meta_wayland_keyboard_broadcast_key (keyboard, event->key.time,
code, is_press);
}
static void
default_grab_modifiers (MetaWaylandKeyboardGrab *grab,
ClutterModifierType modifiers)
{
meta_wayland_keyboard_broadcast_modifiers (grab->keyboard);
}
static const MetaWaylandKeyboardGrabInterface default_keyboard_grab_interface = {
default_grab_key,
default_grab_modifiers
};
void
meta_wayland_keyboard_init (MetaWaylandKeyboard *keyboard,
struct wl_display *display)
@@ -391,6 +450,10 @@ meta_wayland_keyboard_init (MetaWaylandKeyboard *keyboard,
keyboard->xkb_info.keymap_fd = -1;
keyboard->default_grab.interface = &default_keyboard_grab_interface;
keyboard->default_grab.keyboard = keyboard;
keyboard->grab = &keyboard->default_grab;
keyboard->settings = g_settings_new ("org.gnome.desktop.peripherals.keyboard");
g_signal_connect (keyboard->settings, "changed",
G_CALLBACK (settings_changed), keyboard);
@@ -457,10 +520,6 @@ meta_wayland_keyboard_handle_event (MetaWaylandKeyboard *keyboard,
{
gboolean is_press = event->type == CLUTTER_KEY_PRESS;
gboolean handled;
guint32 code;
#ifdef HAVE_NATIVE_BACKEND
MetaBackend *backend = meta_get_backend ();
#endif
/* Synthetic key events are for autorepeat. Ignore those, as
* autorepeat in Wayland is done on the client side. */
@@ -471,14 +530,7 @@ meta_wayland_keyboard_handle_event (MetaWaylandKeyboard *keyboard,
is_press ? "press" : "release",
event->hardware_keycode);
#ifdef HAVE_NATIVE_BACKEND
if (META_IS_BACKEND_NATIVE (backend))
code = clutter_evdev_event_get_event_code ((const ClutterEvent *) event);
else
#endif
code = evdev_code (event);
handled = notify_key (keyboard, event->time, code, is_press);
handled = notify_key (keyboard, (const ClutterEvent *) event);
if (handled)
meta_verbose ("Sent event to wayland client\n");
@@ -696,3 +748,18 @@ meta_wayland_keyboard_can_popup (MetaWaylandKeyboard *keyboard,
{
return keyboard->key_serial == serial;
}
void
meta_wayland_keyboard_start_grab (MetaWaylandKeyboard *keyboard,
MetaWaylandKeyboardGrab *grab)
{
meta_wayland_keyboard_set_focus (keyboard, NULL);
keyboard->grab = grab;
grab->keyboard = keyboard;
}
void
meta_wayland_keyboard_end_grab (MetaWaylandKeyboard *keyboard)
{
keyboard->grab = &keyboard->default_grab;
}

View File

@@ -49,6 +49,20 @@
#include <wayland-server.h>
#include <xkbcommon/xkbcommon.h>
struct _MetaWaylandKeyboardGrabInterface
{
gboolean (*key) (MetaWaylandKeyboardGrab *grab,
const ClutterEvent *event);
void (*modifiers) (MetaWaylandKeyboardGrab *grab,
ClutterModifierType modifiers);
};
struct _MetaWaylandKeyboardGrab
{
const MetaWaylandKeyboardGrabInterface *interface;
MetaWaylandKeyboard *keyboard;
};
typedef struct
{
struct xkb_keymap *keymap;
@@ -73,6 +87,9 @@ struct _MetaWaylandKeyboard
MetaWaylandXkbInfo xkb_info;
enum xkb_state_component mods_changed;
MetaWaylandKeyboardGrab *grab;
MetaWaylandKeyboardGrab default_grab;
GSettings *settings;
};
@@ -104,4 +121,8 @@ void meta_wayland_keyboard_create_new_resource (MetaWaylandKeyboard *keyboard,
gboolean meta_wayland_keyboard_can_popup (MetaWaylandKeyboard *keyboard,
uint32_t serial);
void meta_wayland_keyboard_start_grab (MetaWaylandKeyboard *keyboard,
MetaWaylandKeyboardGrab *grab);
void meta_wayland_keyboard_end_grab (MetaWaylandKeyboard *keyboard);
#endif /* META_WAYLAND_KEYBOARD_H */

View File

@@ -0,0 +1,818 @@
/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
/*
* Copyright (C) 2015 Red Hat
*
* 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.
*
* Written by:
* Jonas Ådahl <jadahl@gmail.com>
*/
#include "config.h"
#include "meta-wayland-pointer-constraints.h"
#include <glib.h>
#include "meta/meta-backend.h"
#include "meta-wayland-private.h"
#include "meta-wayland-seat.h"
#include "meta-wayland-pointer.h"
#include "meta-wayland-surface.h"
#include "meta-wayland-region.h"
#include "meta-pointer-lock-wayland.h"
#include "meta-pointer-confinement-wayland.h"
#include "window-private.h"
#include "backends/meta-backend-private.h"
#include "backends/native/meta-backend-native.h"
#include "backends/meta-pointer-constraint.h"
#include "pointer-constraints-unstable-v1-server-protocol.h"
static GQuark quark_pending_constraint_state = 0;
struct _MetaWaylandPointerConstraint
{
GObject parent;
MetaWaylandSurface *surface;
gboolean is_enabled;
cairo_region_t *region;
struct wl_resource *resource;
MetaWaylandPointerGrab grab;
MetaWaylandSeat *seat;
enum zwp_pointer_constraints_v1_lifetime lifetime;
gboolean hint_set;
wl_fixed_t x_hint;
wl_fixed_t y_hint;
MetaPointerConstraint *constraint;
};
typedef struct
{
MetaWaylandPointerConstraint *constraint;
cairo_region_t *region;
gulong applied_handler_id;
} MetaWaylandPendingConstraintState;
typedef struct
{
GList *pending_constraint_states;
} MetaWaylandPendingConstraintStateContainer;
G_DEFINE_TYPE (MetaWaylandPointerConstraint, meta_wayland_pointer_constraint,
G_TYPE_OBJECT);
static const struct zwp_locked_pointer_v1_interface locked_pointer_interface;
static const struct zwp_confined_pointer_v1_interface confined_pointer_interface;
static const MetaWaylandPointerGrabInterface locked_pointer_grab_interface;
static const MetaWaylandPointerGrabInterface confined_pointer_grab_interface;
static cairo_region_t *
create_infinite_constraint_region (void)
{
return cairo_region_create_rectangle (&(cairo_rectangle_int_t) {
.x = INT_MIN / 2,
.y = INT_MIN / 2,
.width = INT_MAX,
.height = INT_MAX,
});
}
static MetaWaylandPointerConstraint *
meta_wayland_pointer_constraint_new (MetaWaylandSurface *surface,
MetaWaylandSeat *seat,
MetaWaylandRegion *region,
enum zwp_pointer_constraints_v1_lifetime lifetime,
struct wl_resource *resource,
const MetaWaylandPointerGrabInterface *grab_interface)
{
MetaWaylandPointerConstraint *constraint;
constraint = g_object_new (META_TYPE_WAYLAND_POINTER_CONSTRAINT, NULL);
if (!constraint)
return NULL;
constraint->surface = surface;
constraint->seat = seat;
constraint->lifetime = lifetime;
constraint->resource = resource;
constraint->grab.interface = grab_interface;
if (region)
{
constraint->region =
cairo_region_copy (meta_wayland_region_peek_cairo_region (region));
}
else
{
constraint->region = create_infinite_constraint_region ();
}
return constraint;
}
static gboolean
meta_wayland_pointer_constraint_is_enabled (MetaWaylandPointerConstraint *constraint)
{
return constraint->is_enabled;
}
static void
meta_wayland_pointer_constraint_notify_activated (MetaWaylandPointerConstraint *constraint)
{
struct wl_resource *resource = constraint->resource;
if (wl_resource_instance_of (resource,
&zwp_locked_pointer_v1_interface,
&locked_pointer_interface))
{
zwp_locked_pointer_v1_send_locked (resource);
}
else if (wl_resource_instance_of (resource,
&zwp_confined_pointer_v1_interface,
&confined_pointer_interface))
{
zwp_confined_pointer_v1_send_confined (resource);
}
}
static void
meta_wayland_pointer_constraint_notify_deactivated (MetaWaylandPointerConstraint *constraint)
{
struct wl_resource *resource = constraint->resource;
if (wl_resource_instance_of (resource,
&zwp_locked_pointer_v1_interface,
&locked_pointer_interface))
zwp_locked_pointer_v1_send_unlocked (resource);
else if (wl_resource_instance_of (resource,
&zwp_confined_pointer_v1_interface,
&confined_pointer_interface))
zwp_confined_pointer_v1_send_unconfined (resource);
}
static MetaPointerConstraint *
meta_wayland_pointer_constraint_create_pointer_constraint (MetaWaylandPointerConstraint *constraint)
{
struct wl_resource *resource = constraint->resource;
if (wl_resource_instance_of (resource,
&zwp_locked_pointer_v1_interface,
&locked_pointer_interface))
{
return meta_pointer_lock_wayland_new ();
}
else if (wl_resource_instance_of (resource,
&zwp_confined_pointer_v1_interface,
&confined_pointer_interface))
{
return meta_pointer_confinement_wayland_new (constraint);
}
g_assert_not_reached ();
return NULL;
}
static void
meta_wayland_pointer_constraint_enable (MetaWaylandPointerConstraint *constraint)
{
MetaBackend *backend = meta_get_backend ();
g_assert (!constraint->is_enabled);
constraint->is_enabled = TRUE;
meta_wayland_pointer_constraint_notify_activated (constraint);
meta_wayland_pointer_start_grab (&constraint->seat->pointer,
&constraint->grab);
constraint->constraint =
meta_wayland_pointer_constraint_create_pointer_constraint (constraint);
meta_backend_set_client_pointer_constraint (backend, constraint->constraint);
g_object_add_weak_pointer (G_OBJECT (constraint->constraint),
(gpointer *) &constraint->constraint);
g_object_unref (constraint->constraint);
}
static void
meta_wayland_pointer_constraint_disable (MetaWaylandPointerConstraint *constraint)
{
meta_wayland_pointer_constraint_notify_deactivated (constraint);
meta_wayland_pointer_end_grab (constraint->grab.pointer);
meta_backend_set_client_pointer_constraint (meta_get_backend (), NULL);
}
void
meta_wayland_pointer_constraint_destroy (MetaWaylandPointerConstraint *constraint)
{
if (meta_wayland_pointer_constraint_is_enabled (constraint))
meta_wayland_pointer_constraint_disable (constraint);
wl_resource_set_user_data (constraint->resource, NULL);
cairo_region_destroy (constraint->region);
g_object_unref (constraint);
}
static gboolean
is_within_constraint_region (MetaWaylandPointerConstraint *constraint,
wl_fixed_t sx,
wl_fixed_t sy)
{
cairo_region_t *region;
gboolean is_within;
region = meta_wayland_pointer_constraint_calculate_effective_region (constraint);
is_within = cairo_region_contains_point (constraint->region,
wl_fixed_to_int (sx),
wl_fixed_to_int (sy));
cairo_region_destroy (region);
return is_within;
}
void
meta_wayland_pointer_constraint_maybe_enable (MetaWaylandPointerConstraint *constraint)
{
MetaWaylandSeat *seat = constraint->seat;
wl_fixed_t sx, sy;
if (constraint->is_enabled)
return;
if (seat->keyboard.focus_surface != constraint->surface)
return;
meta_wayland_pointer_get_relative_coordinates (&constraint->seat->pointer,
constraint->surface,
&sx, &sy);
if (!is_within_constraint_region (constraint, sx, sy))
return;
meta_wayland_pointer_constraint_enable (constraint);
}
static void
meta_wayland_pointer_constraint_remove (MetaWaylandPointerConstraint *constraint)
{
MetaWaylandSurface *surface = constraint->surface;
meta_wayland_surface_remove_pointer_constraint (surface, constraint);
meta_wayland_pointer_constraint_destroy (constraint);
}
void
meta_wayland_pointer_constraint_maybe_remove_for_seat (MetaWaylandSeat *seat,
MetaWindow *focus_window)
{
MetaWaylandPointer *pointer = &seat->pointer;
if ((pointer->grab->interface == &confined_pointer_grab_interface ||
pointer->grab->interface == &locked_pointer_grab_interface) &&
pointer->focus_surface &&
pointer->focus_surface->window != focus_window)
{
MetaWaylandPointerConstraint *constraint =
wl_container_of (pointer->grab, constraint, grab);
switch (constraint->lifetime)
{
case ZWP_POINTER_CONSTRAINTS_V1_LIFETIME_ONESHOT:
meta_wayland_pointer_constraint_remove (constraint);
break;
case ZWP_POINTER_CONSTRAINTS_V1_LIFETIME_PERSISTENT:
meta_wayland_pointer_constraint_disable (constraint);
break;
default:
g_assert_not_reached ();
}
}
}
void
meta_wayland_pointer_constraint_maybe_enable_for_window (MetaWindow *window)
{
MetaWaylandSurface *surface = window->surface;
GList *it;
for (it = surface->pointer_constraints; it; it = it->next)
{
MetaWaylandPointerConstraint *constraint = it->data;
meta_wayland_pointer_constraint_maybe_enable (constraint);
}
}
MetaWaylandSeat *
meta_wayland_pointer_constraint_get_seat (MetaWaylandPointerConstraint *constraint)
{
return constraint->seat;
}
cairo_region_t *
meta_wayland_pointer_constraint_calculate_effective_region (MetaWaylandPointerConstraint *constraint)
{
cairo_region_t *region;
region = cairo_region_copy (constraint->surface->input_region);
cairo_region_intersect (region, constraint->region);
return region;
}
cairo_region_t *
meta_wayland_pointer_constraint_get_region (MetaWaylandPointerConstraint *constraint)
{
return constraint->region;
}
MetaWaylandSurface *
meta_wayland_pointer_constraint_get_surface (MetaWaylandPointerConstraint *constraint)
{
return constraint->surface;
}
static void
pointer_constraint_resource_destroyed (struct wl_resource *resource)
{
MetaWaylandPointerConstraint *constraint =
wl_resource_get_user_data (resource);
if (!constraint)
return;
meta_wayland_pointer_constraint_remove (constraint);
}
static void
pending_constraint_state_free (MetaWaylandPendingConstraintState *constraint_pending)
{
g_clear_pointer (&constraint_pending->region, cairo_region_destroy);
if (constraint_pending->constraint)
g_object_remove_weak_pointer (G_OBJECT (constraint_pending->constraint),
(gpointer *) &constraint_pending->constraint);
}
static MetaWaylandPendingConstraintStateContainer *
get_pending_constraint_state_container (MetaWaylandPendingState *pending)
{
return g_object_get_qdata (G_OBJECT (pending),
quark_pending_constraint_state);
}
static MetaWaylandPendingConstraintState *
get_pending_constraint_state (MetaWaylandPointerConstraint *constraint)
{
MetaWaylandPendingState *pending = constraint->surface->pending;
MetaWaylandPendingConstraintStateContainer *container;
GList *l;
container = get_pending_constraint_state_container (pending);
for (l = container->pending_constraint_states; l; l = l->next)
{
MetaWaylandPendingConstraintState *constraint_pending = l->data;
if (constraint_pending->constraint == constraint)
return constraint_pending;
}
return NULL;
}
static void
pending_constraint_state_container_free (MetaWaylandPendingConstraintStateContainer *container)
{
g_list_free_full (container->pending_constraint_states,
(GDestroyNotify) pending_constraint_state_free);
g_free (container);
}
static MetaWaylandPendingConstraintStateContainer *
ensure_pending_constraint_state_container (MetaWaylandPendingState *pending)
{
MetaWaylandPendingConstraintStateContainer *container;
container = get_pending_constraint_state_container (pending);
if (!container)
{
container = g_new0 (MetaWaylandPendingConstraintStateContainer, 1);
g_object_set_qdata_full (G_OBJECT (pending),
quark_pending_constraint_state,
container,
(GDestroyNotify) pending_constraint_state_container_free);
}
return container;
}
static void
remove_pending_constraint_state (MetaWaylandPointerConstraint *constraint,
MetaWaylandPendingState *pending)
{
MetaWaylandPendingConstraintStateContainer *container;
GList *l;
container = get_pending_constraint_state_container (pending);
for (l = container->pending_constraint_states; l; l = l->next)
{
MetaWaylandPendingConstraintState *constraint_pending = l->data;
if (constraint_pending->constraint != constraint)
continue;
pending_constraint_state_free (l->data);
container->pending_constraint_states =
g_list_remove_link (container->pending_constraint_states, l);
break;
}
}
static void
pending_constraint_state_applied (MetaWaylandPendingState *pending,
MetaWaylandPendingConstraintState *constraint_pending)
{
MetaWaylandPointerConstraint *constraint = constraint_pending->constraint;
if (!constraint)
return;
g_clear_pointer (&constraint->region, cairo_region_destroy);
if (constraint_pending->region)
{
constraint->region = constraint_pending->region;
constraint_pending->region = NULL;
}
else
{
constraint->region = create_infinite_constraint_region ();
}
g_signal_handler_disconnect (pending,
constraint_pending->applied_handler_id);
remove_pending_constraint_state (constraint, pending);
/* The pointer is potentially warped by the actor paint signal callback if
* the new region proved it necessary.
*/
}
static MetaWaylandPendingConstraintState *
ensure_pending_constraint_state (MetaWaylandPointerConstraint *constraint)
{
MetaWaylandPendingState *pending = constraint->surface->pending;
MetaWaylandPendingConstraintStateContainer *container;
MetaWaylandPendingConstraintState *constraint_pending;
container = ensure_pending_constraint_state_container (pending);
constraint_pending = get_pending_constraint_state (constraint);
if (!constraint_pending)
{
constraint_pending = g_new0 (MetaWaylandPendingConstraintState, 1);
constraint_pending->constraint = constraint;
constraint_pending->applied_handler_id =
g_signal_connect (pending, "applied",
G_CALLBACK (pending_constraint_state_applied),
constraint_pending);
g_object_add_weak_pointer (G_OBJECT (constraint),
(gpointer *) &constraint_pending->constraint);
container->pending_constraint_states =
g_list_append (container->pending_constraint_states,
constraint_pending);
}
return constraint_pending;
}
static void
meta_wayland_pointer_constraint_set_pending_region (MetaWaylandPointerConstraint *constraint,
MetaWaylandRegion *region)
{
MetaWaylandPendingConstraintState *constraint_pending;
constraint_pending = ensure_pending_constraint_state (constraint);
g_clear_pointer (&constraint_pending->region, cairo_region_destroy);
if (region)
{
constraint_pending->region =
cairo_region_copy (meta_wayland_region_peek_cairo_region (region));
}
}
static void
init_pointer_constraint (struct wl_resource *resource,
uint32_t id,
MetaWaylandSurface *surface,
MetaWaylandSeat *seat,
MetaWaylandRegion *region,
enum zwp_pointer_constraints_v1_lifetime lifetime,
const struct wl_interface *interface,
const void *implementation,
const MetaWaylandPointerGrabInterface *grab_interface)
{
struct wl_client *client = wl_resource_get_client (resource);
struct wl_resource *cr;
MetaWaylandPointerConstraint *constraint;
if (meta_wayland_surface_get_pointer_constraint_for_seat (surface, seat))
{
wl_resource_post_error (resource,
WL_DISPLAY_ERROR_INVALID_OBJECT,
"the pointer as already requested to be "
"locked or confined on that surface");
return;
}
cr = wl_resource_create (client, interface,
wl_resource_get_version (resource),
id);
if (cr == NULL)
{
wl_client_post_no_memory (client);
return;
}
constraint = meta_wayland_pointer_constraint_new (surface, seat,
region,
lifetime,
cr, grab_interface);
if (constraint == NULL)
{
wl_client_post_no_memory (client);
return;
}
meta_wayland_surface_add_pointer_constraint (surface, constraint);
wl_resource_set_implementation (cr, implementation, constraint,
pointer_constraint_resource_destroyed);
meta_wayland_pointer_constraint_maybe_enable (constraint);
}
static void
locked_pointer_destroy (struct wl_client *client,
struct wl_resource *resource)
{
MetaWaylandPointerConstraint *constraint =
wl_resource_get_user_data (resource);
gboolean warp_pointer = FALSE;
int warp_x, warp_y;
if (constraint && constraint->is_enabled && constraint->hint_set &&
is_within_constraint_region (constraint,
constraint->x_hint,
constraint->y_hint))
{
float sx, sy;
float x, y;
sx = (float)wl_fixed_to_double (constraint->x_hint);
sy = (float)wl_fixed_to_double (constraint->y_hint);
meta_wayland_surface_get_absolute_coordinates (constraint->surface,
sx, sy,
&x, &y);
warp_pointer = TRUE;
warp_x = (int) x;
warp_y = (int) y;
}
wl_resource_destroy (resource);
if (warp_pointer)
meta_backend_warp_pointer (meta_get_backend (), warp_x, warp_y);
}
static void
locked_pointer_set_cursor_position_hint (struct wl_client *client,
struct wl_resource *resource,
wl_fixed_t surface_x,
wl_fixed_t surface_y)
{
MetaWaylandPointerConstraint *constraint =
wl_resource_get_user_data (resource);
/* Ignore a set cursor hint that was already sent after the constraint
* was cancelled. */
if (!constraint->resource || constraint->resource != resource)
return;
constraint->hint_set = TRUE;
constraint->x_hint = surface_x;
constraint->y_hint = surface_y;
}
static void
locked_pointer_set_region (struct wl_client *client,
struct wl_resource *resource,
struct wl_resource *region_resource)
{
MetaWaylandPointerConstraint *constraint =
wl_resource_get_user_data (resource);
MetaWaylandRegion *region =
region_resource ? wl_resource_get_user_data (region_resource) : NULL;
meta_wayland_pointer_constraint_set_pending_region (constraint, region);
}
static const struct zwp_locked_pointer_v1_interface locked_pointer_interface = {
locked_pointer_destroy,
locked_pointer_set_cursor_position_hint,
locked_pointer_set_region,
};
static void
locked_pointer_grab_pointer_focus (MetaWaylandPointerGrab *grab,
MetaWaylandSurface *surface)
{
}
static void
locked_pointer_grab_pointer_motion (MetaWaylandPointerGrab *grab,
const ClutterEvent *event)
{
meta_wayland_pointer_send_relative_motion (grab->pointer, event);
}
static void
locked_pointer_grab_pointer_button (MetaWaylandPointerGrab *grab,
const ClutterEvent *event)
{
meta_wayland_pointer_send_button (grab->pointer, event);
}
static const MetaWaylandPointerGrabInterface locked_pointer_grab_interface = {
locked_pointer_grab_pointer_focus,
locked_pointer_grab_pointer_motion,
locked_pointer_grab_pointer_button,
};
static void
pointer_constraints_destroy (struct wl_client *client,
struct wl_resource *resource)
{
wl_resource_destroy (resource);
}
static void
pointer_constraints_lock_pointer (struct wl_client *client,
struct wl_resource *resource,
uint32_t id,
struct wl_resource *surface_resource,
struct wl_resource *pointer_resource,
struct wl_resource *region_resource,
uint32_t lifetime)
{
MetaWaylandSurface *surface = wl_resource_get_user_data (surface_resource);
MetaWaylandPointer *pointer = wl_resource_get_user_data (pointer_resource);
MetaWaylandSeat *seat = meta_wayland_pointer_get_seat (pointer);
MetaWaylandRegion *region =
region_resource ? wl_resource_get_user_data (region_resource) : NULL;
init_pointer_constraint (resource, id, surface, seat, region, lifetime,
&zwp_locked_pointer_v1_interface,
&locked_pointer_interface,
&locked_pointer_grab_interface);
}
static void
confined_pointer_grab_pointer_focus (MetaWaylandPointerGrab *grab,
MetaWaylandSurface *surface)
{
}
static void
confined_pointer_grab_pointer_motion (MetaWaylandPointerGrab *grab,
const ClutterEvent *event)
{
MetaWaylandPointerConstraint *constraint =
wl_container_of (grab, constraint, grab);
MetaWaylandPointer *pointer = grab->pointer;
g_assert (pointer->focus_surface);
g_assert (pointer->focus_surface == constraint->surface);
meta_wayland_pointer_send_motion (pointer, event);
}
static void
confined_pointer_grab_pointer_button (MetaWaylandPointerGrab *grab,
const ClutterEvent *event)
{
meta_wayland_pointer_send_button (grab->pointer, event);
}
static const MetaWaylandPointerGrabInterface confined_pointer_grab_interface = {
confined_pointer_grab_pointer_focus,
confined_pointer_grab_pointer_motion,
confined_pointer_grab_pointer_button,
};
static void
confined_pointer_destroy (struct wl_client *client,
struct wl_resource *resource)
{
wl_resource_destroy (resource);
}
static void
confined_pointer_set_region (struct wl_client *client,
struct wl_resource *resource,
struct wl_resource *region_resource)
{
MetaWaylandPointerConstraint *constraint =
wl_resource_get_user_data (resource);
MetaWaylandRegion *region =
region_resource ? wl_resource_get_user_data (region_resource) : NULL;
meta_wayland_pointer_constraint_set_pending_region (constraint, region);
}
static const struct zwp_confined_pointer_v1_interface confined_pointer_interface = {
confined_pointer_destroy,
confined_pointer_set_region,
};
static void
pointer_constraints_confine_pointer (struct wl_client *client,
struct wl_resource *resource,
uint32_t id,
struct wl_resource *surface_resource,
struct wl_resource *pointer_resource,
struct wl_resource *region_resource,
uint32_t lifetime)
{
MetaWaylandSurface *surface = wl_resource_get_user_data (surface_resource);
MetaWaylandPointer *pointer = wl_resource_get_user_data (pointer_resource);
MetaWaylandSeat *seat = meta_wayland_pointer_get_seat (pointer);
MetaWaylandRegion *region =
region_resource ? wl_resource_get_user_data (region_resource) : NULL;
init_pointer_constraint (resource, id, surface, seat, region, lifetime,
&zwp_confined_pointer_v1_interface,
&confined_pointer_interface,
&confined_pointer_grab_interface);
}
static const struct zwp_pointer_constraints_v1_interface pointer_constraints = {
pointer_constraints_destroy,
pointer_constraints_lock_pointer,
pointer_constraints_confine_pointer,
};
static void
bind_pointer_constraints (struct wl_client *client,
void *data,
uint32_t version,
uint32_t id)
{
MetaWaylandCompositor *compositor = data;
struct wl_resource *resource;
resource = wl_resource_create (client,
&zwp_pointer_constraints_v1_interface,
1, id);
wl_resource_set_implementation (resource,
&pointer_constraints,
compositor,
NULL);
}
void
meta_wayland_pointer_constraints_init (MetaWaylandCompositor *compositor)
{
if (!wl_global_create (compositor->wayland_display,
&zwp_pointer_constraints_v1_interface, 1,
compositor, bind_pointer_constraints))
g_error ("Could not create wp_pointer_constraints global");
}
static void
meta_wayland_pointer_constraint_init (MetaWaylandPointerConstraint *constraint)
{
}
static void
meta_wayland_pointer_constraint_class_init (MetaWaylandPointerConstraintClass *klass)
{
quark_pending_constraint_state =
g_quark_from_static_string ("-meta-wayland-pointer-constraint-pending_state");
}

View File

@@ -0,0 +1,60 @@
/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
/*
* Copyright (C) 2015 Red Hat
*
* 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.
*
* Written by:
* Jonas Ådahl <jadahl@gmail.com>
*/
#ifndef META_WAYLAND_POINTER_CONSTRAINTS_H
#define META_WAYLAND_POINTER_CONSTRAINTS_H
#include "meta-wayland-types.h"
#include "meta/window.h"
#include <wayland-server.h>
#define META_TYPE_WAYLAND_POINTER_CONSTRAINT (meta_wayland_pointer_constraint_get_type ())
G_DECLARE_FINAL_TYPE (MetaWaylandPointerConstraint,
meta_wayland_pointer_constraint,
META, WAYLAND_POINTER_CONSTRAINT,
GObject);
typedef struct _MetaWaylandPointerConstraint MetaWaylandPointerConstraint;
void meta_wayland_pointer_constraints_init (MetaWaylandCompositor *compositor);
void meta_wayland_pointer_constraint_maybe_enable (MetaWaylandPointerConstraint *constraint);
void meta_wayland_pointer_constraint_destroy (MetaWaylandPointerConstraint *constraint);
MetaWaylandSeat * meta_wayland_pointer_constraint_get_seat (MetaWaylandPointerConstraint *constraint);
cairo_region_t * meta_wayland_pointer_constraint_calculate_effective_region (MetaWaylandPointerConstraint *constraint);
cairo_region_t * meta_wayland_pointer_constraint_get_region (MetaWaylandPointerConstraint *constraint);
MetaWaylandSurface * meta_wayland_pointer_constraint_get_surface (MetaWaylandPointerConstraint *constraint);
void meta_wayland_pointer_constraint_maybe_remove_for_seat (MetaWaylandSeat *seat,
MetaWindow *focus_window);
void meta_wayland_pointer_constraint_maybe_enable_for_window (MetaWindow *window);
#endif /* META_WAYLAND_POINTER_CONSTRAINTS_H */

View File

@@ -63,6 +63,8 @@
#include "backends/meta-cursor-tracker-private.h"
#include "backends/meta-cursor-renderer.h"
#include "relative-pointer-unstable-v1-server-protocol.h"
#ifdef HAVE_NATIVE_BACKEND
#include "backends/native/meta-backend-native.h"
#endif
@@ -96,6 +98,7 @@ meta_wayland_pointer_client_new (void)
wl_list_init (&pointer_client->pointer_resources);
wl_list_init (&pointer_client->swipe_gesture_resources);
wl_list_init (&pointer_client->pinch_gesture_resources);
wl_list_init (&pointer_client->relative_pointer_resources);
return pointer_client;
}
@@ -124,6 +127,11 @@ meta_wayland_pointer_client_free (MetaWaylandPointerClient *pointer_client)
wl_list_remove (wl_resource_get_link (resource));
wl_list_init (wl_resource_get_link (resource));
}
wl_resource_for_each_safe (resource, next, &pointer_client->relative_pointer_resources)
{
wl_list_remove (wl_resource_get_link (resource));
wl_list_init (wl_resource_get_link (resource));
}
g_slice_free (MetaWaylandPointerClient, pointer_client);
}
@@ -133,7 +141,8 @@ meta_wayland_pointer_client_is_empty (MetaWaylandPointerClient *pointer_client)
{
return (wl_list_empty (&pointer_client->pointer_resources) &&
wl_list_empty (&pointer_client->swipe_gesture_resources) &&
wl_list_empty (&pointer_client->pinch_gesture_resources));
wl_list_empty (&pointer_client->pinch_gesture_resources) &&
wl_list_empty (&pointer_client->relative_pointer_resources));
}
MetaWaylandPointerClient *
@@ -242,26 +251,102 @@ pointer_handle_focus_surface_destroy (struct wl_listener *listener, void *data)
meta_wayland_pointer_set_focus (pointer, NULL);
}
static void
meta_wayland_pointer_send_frame (MetaWaylandPointer *pointer,
struct wl_resource *resource)
{
if (wl_resource_get_version (resource) >= WL_POINTER_AXIS_SOURCE_SINCE_VERSION)
wl_pointer_send_frame (resource);
}
static void
meta_wayland_pointer_broadcast_frame (MetaWaylandPointer *pointer)
{
struct wl_resource *resource;
if (!pointer->focus_client)
return;
wl_resource_for_each (resource, &pointer->focus_client->pointer_resources)
{
meta_wayland_pointer_send_frame (pointer, resource);
}
}
void
meta_wayland_pointer_send_relative_motion (MetaWaylandPointer *pointer,
const ClutterEvent *event)
{
struct wl_resource *resource;
double dx, dy;
double dx_unaccel, dy_unaccel;
uint64_t time_us;
uint32_t time_us_hi;
uint32_t time_us_lo;
wl_fixed_t dxf, dyf;
wl_fixed_t dx_unaccelf, dy_unaccelf;
if (!pointer->focus_client)
return;
if (!meta_backend_get_relative_motion_deltas (meta_get_backend (),
event,
&dx, &dy,
&dx_unaccel, &dy_unaccel))
return;
#ifdef HAVE_NATIVE_BACKEND
time_us = clutter_evdev_event_get_time_usec (event);
if (time_us == 0)
#endif
time_us = clutter_event_get_time (event) * 1000ULL;
time_us_hi = (uint32_t) (time_us >> 32);
time_us_lo = (uint32_t) time_us;
dxf = wl_fixed_from_double (dx);
dyf = wl_fixed_from_double (dy);
dx_unaccelf = wl_fixed_from_double (dx_unaccel);
dy_unaccelf = wl_fixed_from_double (dy_unaccel);
wl_resource_for_each (resource,
&pointer->focus_client->relative_pointer_resources)
{
zwp_relative_pointer_v1_send_relative_motion (resource,
time_us_hi,
time_us_lo,
dxf,
dyf,
dx_unaccelf,
dy_unaccelf);
}
}
void
meta_wayland_pointer_send_motion (MetaWaylandPointer *pointer,
const ClutterEvent *event)
{
struct wl_resource *resource;
uint32_t time;
wl_fixed_t sx, sy;
float sx, sy;
if (!pointer->focus_client)
return;
time = clutter_event_get_time (event);
meta_wayland_pointer_get_relative_coordinates (pointer,
pointer->focus_surface,
meta_wayland_surface_get_relative_coordinates (pointer->focus_surface,
event->motion.x,
event->motion.y,
&sx, &sy);
wl_resource_for_each (resource, &pointer->focus_client->pointer_resources)
{
wl_pointer_send_motion (resource, time, sx, sy);
wl_pointer_send_motion (resource, time,
wl_fixed_from_double (sx),
wl_fixed_from_double (sy));
}
meta_wayland_pointer_send_relative_motion (pointer, event);
meta_wayland_pointer_broadcast_frame (pointer);
}
void
@@ -321,6 +406,8 @@ meta_wayland_pointer_send_button (MetaWaylandPointer *pointer,
time, button,
event_type == CLUTTER_BUTTON_PRESS ? 1 : 0);
}
meta_wayland_pointer_broadcast_frame (pointer);
}
if (pointer->button_count == 0 && event_type == CLUTTER_BUTTON_RELEASE)
@@ -510,26 +597,48 @@ handle_scroll_event (MetaWaylandPointer *pointer,
{
struct wl_resource *resource;
wl_fixed_t x_value = 0, y_value = 0;
int x_discrete = 0, y_discrete = 0;
enum wl_pointer_axis_source source = -1;
if (clutter_event_is_pointer_emulated (event))
return;
switch (event->scroll.scroll_source)
{
case CLUTTER_SCROLL_SOURCE_WHEEL:
source = WL_POINTER_AXIS_SOURCE_WHEEL;
break;
case CLUTTER_SCROLL_SOURCE_FINGER:
source = WL_POINTER_AXIS_SOURCE_FINGER;
break;
case CLUTTER_SCROLL_SOURCE_CONTINUOUS:
source = WL_POINTER_AXIS_SOURCE_CONTINUOUS;
break;
default:
source = WL_POINTER_AXIS_SOURCE_WHEEL;
break;
}
switch (clutter_event_get_scroll_direction (event))
{
case CLUTTER_SCROLL_UP:
y_value = -DEFAULT_AXIS_STEP_DISTANCE;
y_discrete = -1;
break;
case CLUTTER_SCROLL_DOWN:
y_value = DEFAULT_AXIS_STEP_DISTANCE;
y_discrete = 1;
break;
case CLUTTER_SCROLL_LEFT:
x_value = -DEFAULT_AXIS_STEP_DISTANCE;
x_discrete = -1;
break;
case CLUTTER_SCROLL_RIGHT:
x_value = DEFAULT_AXIS_STEP_DISTANCE;
x_discrete = 1;
break;
case CLUTTER_SCROLL_SMOOTH:
@@ -553,13 +662,44 @@ handle_scroll_event (MetaWaylandPointer *pointer,
{
wl_resource_for_each (resource, &pointer->focus_client->pointer_resources)
{
if (wl_resource_get_version (resource) >= WL_POINTER_AXIS_SOURCE_SINCE_VERSION)
wl_pointer_send_axis_source (resource, source);
/* X axis */
if (x_discrete != 0 &&
wl_resource_get_version (resource) >= WL_POINTER_AXIS_DISCRETE_SINCE_VERSION)
wl_pointer_send_axis_discrete (resource,
WL_POINTER_AXIS_HORIZONTAL_SCROLL,
x_discrete);
if (x_value)
wl_pointer_send_axis (resource, clutter_event_get_time (event),
WL_POINTER_AXIS_HORIZONTAL_SCROLL, x_value);
if ((event->scroll.finish_flags & CLUTTER_SCROLL_FINISHED_HORIZONTAL) &&
wl_resource_get_version (resource) >= WL_POINTER_AXIS_STOP_SINCE_VERSION)
wl_pointer_send_axis_stop (resource,
clutter_event_get_time (event),
WL_POINTER_AXIS_HORIZONTAL_SCROLL);
/* Y axis */
if (y_discrete != 0 &&
wl_resource_get_version (resource) >= WL_POINTER_AXIS_DISCRETE_SINCE_VERSION)
wl_pointer_send_axis_discrete (resource,
WL_POINTER_AXIS_VERTICAL_SCROLL,
y_discrete);
if (y_value)
wl_pointer_send_axis (resource, clutter_event_get_time (event),
WL_POINTER_AXIS_VERTICAL_SCROLL, y_value);
if ((event->scroll.finish_flags & CLUTTER_SCROLL_FINISHED_VERTICAL) &&
wl_resource_get_version (resource) >= WL_POINTER_AXIS_STOP_SINCE_VERSION)
wl_pointer_send_axis_stop (resource,
clutter_event_get_time (event),
WL_POINTER_AXIS_VERTICAL_SCROLL);
}
meta_wayland_pointer_broadcast_frame (pointer);
}
}
@@ -632,6 +772,8 @@ meta_wayland_pointer_broadcast_enter (MetaWaylandPointer *pointer,
&pointer->focus_client->pointer_resources)
meta_wayland_pointer_send_enter (pointer, pointer_resource,
serial, surface);
meta_wayland_pointer_broadcast_frame (pointer);
}
static void
@@ -645,6 +787,8 @@ meta_wayland_pointer_broadcast_leave (MetaWaylandPointer *pointer,
&pointer->focus_client->pointer_resources)
meta_wayland_pointer_send_leave (pointer, pointer_resource,
serial, surface);
meta_wayland_pointer_broadcast_frame (pointer);
}
void
@@ -783,11 +927,10 @@ meta_wayland_pointer_get_relative_coordinates (MetaWaylandPointer *pointer,
ClutterPoint pos;
clutter_input_device_get_coords (pointer->device, NULL, &pos);
clutter_actor_transform_stage_point (CLUTTER_ACTOR (meta_surface_actor_get_texture (surface->surface_actor)),
pos.x, pos.y, &xf, &yf);
meta_wayland_surface_get_relative_coordinates (surface, pos.x, pos.y, &xf, &yf);
*sx = wl_fixed_from_double (xf) / surface->scale;
*sy = wl_fixed_from_double (yf) / surface->scale;
*sx = wl_fixed_from_double (xf);
*sy = wl_fixed_from_double (yf);
}
static void
@@ -978,9 +1121,12 @@ meta_wayland_pointer_create_new_resource (MetaWaylandPointer *pointer,
wl_resource_get_link (cr));
if (pointer->focus_client == pointer_client)
meta_wayland_pointer_send_enter (pointer, cr,
pointer->focus_serial,
pointer->focus_surface);
{
meta_wayland_pointer_send_enter (pointer, cr,
pointer->focus_serial,
pointer->focus_surface);
meta_wayland_pointer_send_frame (pointer, cr);
}
}
gboolean
@@ -1011,6 +1157,108 @@ meta_wayland_pointer_get_top_popup (MetaWaylandPointer *pointer)
return meta_wayland_popup_grab_get_top_popup(grab);
}
static void
relative_pointer_destroy (struct wl_client *client,
struct wl_resource *resource)
{
wl_resource_destroy (resource);
}
static const struct zwp_relative_pointer_v1_interface relative_pointer_interface = {
relative_pointer_destroy
};
static void
relative_pointer_manager_destroy (struct wl_client *client,
struct wl_resource *resource)
{
wl_resource_destroy (resource);
}
static void
relative_pointer_manager_get_relative_pointer (struct wl_client *client,
struct wl_resource *resource,
uint32_t id,
struct wl_resource *pointer_resource)
{
MetaWaylandPointer *pointer = wl_resource_get_user_data (pointer_resource);
struct wl_resource *cr;
MetaWaylandPointerClient *pointer_client;
cr = wl_resource_create (client, &zwp_relative_pointer_v1_interface,
wl_resource_get_version (resource), id);
if (cr == NULL)
{
wl_client_post_no_memory (client);
return;
}
wl_resource_set_implementation (cr, &relative_pointer_interface,
pointer,
meta_wayland_pointer_unbind_pointer_client_resource);
pointer_client = meta_wayland_pointer_ensure_pointer_client (pointer, client);
wl_list_insert (&pointer_client->relative_pointer_resources,
wl_resource_get_link (cr));
}
static const struct zwp_relative_pointer_manager_v1_interface relative_pointer_manager = {
relative_pointer_manager_destroy,
relative_pointer_manager_get_relative_pointer,
};
static void
bind_relative_pointer_manager (struct wl_client *client,
void *data,
uint32_t version,
uint32_t id)
{
MetaWaylandCompositor *compositor = data;
struct wl_resource *resource;
resource = wl_resource_create (client,
&zwp_relative_pointer_manager_v1_interface,
1, id);
if (version != 1)
wl_resource_post_error (resource,
WL_DISPLAY_ERROR_INVALID_OBJECT,
"bound invalid version %u of "
"wp_relative_pointer_manager",
version);
wl_resource_set_implementation (resource, &relative_pointer_manager,
compositor,
NULL);
}
void
meta_wayland_relative_pointer_init (MetaWaylandCompositor *compositor)
{
/* Relative pointer events are currently only supported by the native backend
* so lets just advertise the extension when the native backend is used.
*/
#ifdef HAVE_NATIVE_BACKEND
if (!META_IS_BACKEND_NATIVE (meta_get_backend ()))
return;
#else
return;
#endif
if (!wl_global_create (compositor->wayland_display,
&zwp_relative_pointer_manager_v1_interface, 1,
compositor, bind_relative_pointer_manager))
g_error ("Could not create relative pointer manager global");
}
MetaWaylandSeat *
meta_wayland_pointer_get_seat (MetaWaylandPointer *pointer)
{
MetaWaylandSeat *seat = wl_container_of (pointer, seat, pointer);
return seat;
}
static void
cursor_surface_role_assigned (MetaWaylandSurfaceRole *surface_role)
{

View File

@@ -28,6 +28,7 @@
#include "meta-wayland-pointer-gesture-swipe.h"
#include "meta-wayland-pointer-gesture-pinch.h"
#include "meta-wayland-surface.h"
#include "meta-wayland-pointer-constraints.h"
#include <meta/meta-cursor-tracker.h>
@@ -58,6 +59,7 @@ struct _MetaWaylandPointerClient
struct wl_list pointer_resources;
struct wl_list swipe_gesture_resources;
struct wl_list pinch_gesture_resources;
struct wl_list relative_pointer_resources;
};
struct _MetaWaylandPointer
@@ -101,6 +103,9 @@ gboolean meta_wayland_pointer_handle_event (MetaWaylandPointer *pointer,
void meta_wayland_pointer_send_motion (MetaWaylandPointer *pointer,
const ClutterEvent *event);
void meta_wayland_pointer_send_relative_motion (MetaWaylandPointer *pointer,
const ClutterEvent *event);
void meta_wayland_pointer_send_button (MetaWaylandPointer *pointer,
const ClutterEvent *event);
@@ -142,4 +147,8 @@ MetaWaylandPointerClient * meta_wayland_pointer_get_pointer_client (MetaWaylandP
struct wl_client *client);
void meta_wayland_pointer_unbind_pointer_client_resource (struct wl_resource *resource);
void meta_wayland_relative_pointer_init (MetaWaylandCompositor *compositor);
MetaWaylandSeat *meta_wayland_pointer_get_seat (MetaWaylandPointer *pointer);
#endif /* META_WAYLAND_POINTER_H */

View File

@@ -55,6 +55,14 @@
#include "meta-surface-actor-wayland.h"
#include "meta-xwayland-private.h"
enum {
PENDING_STATE_SIGNAL_APPLIED,
PENDING_STATE_SIGNAL_LAST_SIGNAL
};
static guint pending_state_signals[PENDING_STATE_SIGNAL_LAST_SIGNAL];
typedef struct _MetaWaylandSurfaceRolePrivate
{
MetaWaylandSurface *surface;
@@ -79,6 +87,10 @@ G_DEFINE_TYPE_WITH_PRIVATE (MetaWaylandSurfaceRole,
meta_wayland_surface_role,
G_TYPE_OBJECT);
G_DEFINE_TYPE (MetaWaylandPendingState,
meta_wayland_pending_state,
G_TYPE_OBJECT);
struct _MetaWaylandSurfaceRoleSubsurface
{
MetaWaylandSurfaceRole parent;
@@ -257,8 +269,6 @@ dnd_surface_commit (MetaWaylandSurfaceRole *surface_role,
meta_wayland_surface_role_get_surface (surface_role);
meta_wayland_surface_queue_pending_state_frame_callbacks (surface, pending);
meta_wayland_data_device_update_dnd_surface (&surface->compositor->seat->data_device);
}
static void
@@ -470,7 +480,18 @@ move_pending_state (MetaWaylandPendingState *from,
if (from->buffer)
wl_list_remove (&from->buffer_destroy_listener.link);
*to = *from;
to->newly_attached = from->newly_attached;
to->buffer = from->buffer;
to->dx = from->dx;
to->dy = from->dy;
to->scale = from->scale;
to->damage = from->damage;
to->input_region = from->input_region;
to->input_region_set = from->input_region_set;
to->opaque_region = from->opaque_region;
to->opaque_region_set = from->opaque_region_set;
to->new_geometry = from->new_geometry;
to->has_new_geometry = from->has_new_geometry;
wl_list_init (&to->frame_callback_list);
wl_list_insert_list (&to->frame_callback_list, &from->frame_callback_list);
@@ -481,6 +502,38 @@ move_pending_state (MetaWaylandPendingState *from,
pending_state_init (from);
}
static void
meta_wayland_pending_state_finalize (GObject *object)
{
MetaWaylandPendingState *state = META_WAYLAND_PENDING_STATE (object);
pending_state_destroy (state);
G_OBJECT_CLASS (meta_wayland_pending_state_parent_class)->finalize (object);
}
static void
meta_wayland_pending_state_init (MetaWaylandPendingState *state)
{
pending_state_init (state);
}
static void
meta_wayland_pending_state_class_init (MetaWaylandPendingStateClass *klass)
{
GObjectClass *object_class = G_OBJECT_CLASS (klass);
object_class->finalize = meta_wayland_pending_state_finalize;
pending_state_signals[PENDING_STATE_SIGNAL_APPLIED] =
g_signal_new ("applied",
G_TYPE_FROM_CLASS (object_class),
G_SIGNAL_RUN_LAST,
0,
NULL, NULL, NULL,
G_TYPE_NONE, 0);
}
static void
subsurface_surface_commit (MetaWaylandSurfaceRole *surface_role,
MetaWaylandPendingState *pending)
@@ -579,7 +632,7 @@ parent_surface_state_applied (gpointer data, gpointer user_data)
}
if (is_surface_effectively_synchronized (surface))
apply_pending_state (surface, &surface->sub.pending);
apply_pending_state (surface, surface->sub.pending);
meta_surface_actor_wayland_sync_subsurface_state (
META_SURFACE_ACTOR_WAYLAND (surface->surface_actor));
@@ -598,6 +651,7 @@ apply_pending_state (MetaWaylandSurface *surface,
if (pending->buffer)
{
meta_wayland_buffer_take_control (pending->buffer);
CoglTexture *texture = meta_wayland_buffer_ensure_texture (pending->buffer);
meta_surface_actor_wayland_set_texture (META_SURFACE_ACTOR_WAYLAND (surface->surface_actor), texture);
}
@@ -609,6 +663,9 @@ apply_pending_state (MetaWaylandSurface *surface,
if (!cairo_region_is_empty (pending->damage))
surface_process_damage (surface, pending->damage);
if (pending->buffer && pending->buffer->copied_data)
meta_wayland_buffer_release_control (pending->buffer);
surface->offset_x += pending->dx;
surface->offset_y += pending->dy;
@@ -648,6 +705,10 @@ apply_pending_state (MetaWaylandSurface *surface,
wl_list_init (&pending->frame_callback_list);
}
g_signal_emit (pending,
pending_state_signals[PENDING_STATE_SIGNAL_APPLIED],
0);
meta_surface_actor_wayland_sync_state (
META_SURFACE_ACTOR_WAYLAND (surface->surface_actor));
@@ -668,9 +729,9 @@ meta_wayland_surface_commit (MetaWaylandSurface *surface)
* surface is in effective desynchronized mode.
*/
if (is_surface_effectively_synchronized (surface))
move_pending_state (&surface->pending, &surface->sub.pending);
move_pending_state (surface->pending, surface->sub.pending);
else
apply_pending_state (surface, &surface->pending);
apply_pending_state (surface, surface->pending);
}
static void
@@ -699,17 +760,17 @@ wl_surface_attach (struct wl_client *client,
else
buffer = NULL;
if (surface->pending.buffer)
wl_list_remove (&surface->pending.buffer_destroy_listener.link);
if (surface->pending->buffer)
wl_list_remove (&surface->pending->buffer_destroy_listener.link);
surface->pending.newly_attached = TRUE;
surface->pending.buffer = buffer;
surface->pending.dx = dx;
surface->pending.dy = dy;
surface->pending->newly_attached = TRUE;
surface->pending->buffer = buffer;
surface->pending->dx = dx;
surface->pending->dy = dy;
if (buffer)
wl_signal_add (&buffer->destroy_signal,
&surface->pending.buffer_destroy_listener);
&surface->pending->buffer_destroy_listener);
}
static void
@@ -727,7 +788,7 @@ wl_surface_damage (struct wl_client *client,
if (!surface)
return;
cairo_region_union_rectangle (surface->pending.damage, &rectangle);
cairo_region_union_rectangle (surface->pending->damage, &rectangle);
}
static void
@@ -757,7 +818,7 @@ wl_surface_frame (struct wl_client *client,
callback->resource = wl_resource_create (client, &wl_callback_interface, META_WL_CALLBACK_VERSION, callback_id);
wl_resource_set_implementation (callback->resource, NULL, callback, destroy_frame_callback);
wl_list_insert (surface->pending.frame_callback_list.prev, &callback->link);
wl_list_insert (surface->pending->frame_callback_list.prev, &callback->link);
}
static void
@@ -771,14 +832,14 @@ wl_surface_set_opaque_region (struct wl_client *client,
if (!surface)
return;
g_clear_pointer (&surface->pending.opaque_region, cairo_region_destroy);
g_clear_pointer (&surface->pending->opaque_region, cairo_region_destroy);
if (region_resource)
{
MetaWaylandRegion *region = wl_resource_get_user_data (region_resource);
cairo_region_t *cr_region = meta_wayland_region_peek_cairo_region (region);
surface->pending.opaque_region = cairo_region_copy (cr_region);
surface->pending->opaque_region = cairo_region_copy (cr_region);
}
surface->pending.opaque_region_set = TRUE;
surface->pending->opaque_region_set = TRUE;
}
static void
@@ -792,14 +853,14 @@ wl_surface_set_input_region (struct wl_client *client,
if (!surface)
return;
g_clear_pointer (&surface->pending.input_region, cairo_region_destroy);
g_clear_pointer (&surface->pending->input_region, cairo_region_destroy);
if (region_resource)
{
MetaWaylandRegion *region = wl_resource_get_user_data (region_resource);
cairo_region_t *cr_region = meta_wayland_region_peek_cairo_region (region);
surface->pending.input_region = cairo_region_copy (cr_region);
surface->pending->input_region = cairo_region_copy (cr_region);
}
surface->pending.input_region_set = TRUE;
surface->pending->input_region_set = TRUE;
}
static void
@@ -830,7 +891,7 @@ wl_surface_set_buffer_scale (struct wl_client *client,
{
MetaWaylandSurface *surface = wl_resource_get_user_data (resource);
if (scale > 0)
surface->pending.scale = scale;
surface->pending->scale = scale;
else
g_warning ("Trying to set invalid buffer_scale of %d\n", scale);
}
@@ -1031,8 +1092,11 @@ wl_surface_destructor (struct wl_resource *resource)
if (surface->window)
destroy_window (surface);
g_list_free_full (surface->pointer_constraints,
(GDestroyNotify) meta_wayland_pointer_constraint_destroy);
surface_set_buffer (surface, NULL);
pending_state_destroy (&surface->pending);
g_clear_object (&surface->pending);
if (surface->opaque_region)
cairo_region_destroy (surface->opaque_region);
@@ -1071,6 +1135,13 @@ wl_surface_destructor (struct wl_resource *resource)
meta_wayland_compositor_repick (compositor);
}
static void
surface_actor_painting (MetaSurfaceActorWayland *surface_actor,
MetaWaylandSurface *surface)
{
meta_wayland_surface_update_outputs (surface);
}
MetaWaylandSurface *
meta_wayland_surface_create (MetaWaylandCompositor *compositor,
struct wl_client *client,
@@ -1090,11 +1161,16 @@ meta_wayland_surface_create (MetaWaylandCompositor *compositor,
wl_list_init (&surface->pending_frame_callback_list);
g_signal_connect_object (surface->surface_actor,
"painting",
G_CALLBACK (surface_actor_painting),
surface,
0);
sync_drag_dest_funcs (surface);
surface->outputs_to_destroy_notify_id = g_hash_table_new (NULL, NULL);
pending_state_init (&surface->pending);
return surface;
}
@@ -1297,11 +1373,11 @@ xdg_surface_set_window_geometry (struct wl_client *client,
{
MetaWaylandSurface *surface = wl_resource_get_user_data (resource);
surface->pending.has_new_geometry = TRUE;
surface->pending.new_geometry.x = x;
surface->pending.new_geometry.y = y;
surface->pending.new_geometry.width = width;
surface->pending.new_geometry.height = height;
surface->pending->has_new_geometry = TRUE;
surface->pending->new_geometry.x = x;
surface->pending->new_geometry.y = y;
surface->pending->new_geometry.width = width;
surface->pending->new_geometry.height = height;
}
static void
@@ -1953,8 +2029,21 @@ get_gtk_surface (struct wl_client *client,
wl_resource_set_implementation (surface->gtk_surface, &meta_wayland_gtk_surface_interface, surface, gtk_surface_destructor);
}
static void
set_startup_id (struct wl_client *client,
struct wl_resource *resource,
const char *startup_id)
{
MetaDisplay *display;
display = meta_get_display ();
meta_startup_notification_remove_sequence (display->startup_notification,
startup_id);
}
static const struct gtk_shell_interface meta_wayland_gtk_shell_interface = {
get_gtk_surface
get_gtk_surface,
set_startup_id
};
static void
@@ -1968,7 +2057,7 @@ bind_gtk_shell (struct wl_client *client,
resource = wl_resource_create (client, &gtk_shell_interface, version, id);
if (version != META_GTK_SHELL_VERSION)
if (version < 2)
{
wl_resource_post_error (resource,
WL_DISPLAY_ERROR_INVALID_OBJECT,
@@ -2010,7 +2099,7 @@ wl_subsurface_destructor (struct wl_resource *resource)
surface->sub.parent = NULL;
}
pending_state_destroy (&surface->sub.pending);
g_clear_object (&surface->sub.pending);
surface->wl_subsurface = NULL;
}
@@ -2136,7 +2225,7 @@ wl_subsurface_set_desync (struct wl_client *client,
surface->sub.synchronous = FALSE;
if (was_effectively_synchronized &&
!is_surface_effectively_synchronized (surface))
apply_pending_state (surface, &surface->sub.pending);
apply_pending_state (surface, surface->sub.pending);
}
static const struct wl_subsurface_interface meta_wayland_wl_subsurface_interface = {
@@ -2200,7 +2289,7 @@ wl_subcompositor_get_subsurface (struct wl_client *client,
surface->wl_subsurface = wl_resource_create (client, &wl_subsurface_interface, wl_resource_get_version (resource), id);
wl_resource_set_implementation (surface->wl_subsurface, &meta_wayland_wl_subsurface_interface, surface, wl_subsurface_destructor);
pending_state_init (&surface->sub.pending);
surface->sub.pending = g_object_new (META_TYPE_WAYLAND_PENDING_STATE, NULL);
surface->sub.synchronous = TRUE;
surface->sub.parent = parent;
surface->sub.parent_destroy_listener.notify = surface_handle_parent_surface_destroyed;
@@ -2387,6 +2476,15 @@ meta_wayland_surface_drag_dest_drop (MetaWaylandSurface *surface)
surface->dnd.funcs->drop (data_device, surface);
}
void
meta_wayland_surface_drag_dest_update (MetaWaylandSurface *surface)
{
MetaWaylandCompositor *compositor = meta_wayland_compositor_get_default ();
MetaWaylandDataDevice *data_device = &compositor->seat->data_device;
surface->dnd.funcs->update (data_device, surface);
}
MetaWindow *
meta_wayland_surface_get_toplevel_window (MetaWaylandSurface *surface)
{
@@ -2406,9 +2504,79 @@ meta_wayland_surface_get_toplevel_window (MetaWaylandSurface *surface)
return NULL;
}
void
meta_wayland_surface_add_pointer_constraint (MetaWaylandSurface *surface,
MetaWaylandPointerConstraint *constraint)
{
surface->pointer_constraints = g_list_append (surface->pointer_constraints,
constraint);
}
void
meta_wayland_surface_remove_pointer_constraint (MetaWaylandSurface *surface,
MetaWaylandPointerConstraint *constraint)
{
surface->pointer_constraints = g_list_remove (surface->pointer_constraints,
constraint);
}
MetaWaylandPointerConstraint *
meta_wayland_surface_get_pointer_constraint_for_seat (MetaWaylandSurface *surface,
MetaWaylandSeat *seat)
{
GList *iter;
for (iter = surface->pointer_constraints; iter; iter = iter->next)
{
MetaWaylandPointerConstraint *constraint = iter->data;
if (seat == meta_wayland_pointer_constraint_get_seat (constraint))
return constraint;
}
return NULL;
}
void
meta_wayland_surface_get_relative_coordinates (MetaWaylandSurface *surface,
float abs_x,
float abs_y,
float *sx,
float *sy)
{
ClutterActor *actor =
CLUTTER_ACTOR (meta_surface_actor_get_texture (surface->surface_actor));
clutter_actor_transform_stage_point (actor, abs_x, abs_y, sx, sy);
*sx /= surface->scale;
*sy /= surface->scale;
}
void
meta_wayland_surface_get_absolute_coordinates (MetaWaylandSurface *surface,
float sx,
float sy,
float *x,
float *y)
{
ClutterActor *actor =
CLUTTER_ACTOR (meta_surface_actor_get_texture (surface->surface_actor));
ClutterVertex sv = {
.x = sx * surface->scale,
.y = sy * surface->scale,
};
ClutterVertex v = { 0 };
clutter_actor_apply_relative_transform_to_point (actor, NULL, &sv, &v);
*x = v.x;
*y = v.y;
}
static void
meta_wayland_surface_init (MetaWaylandSurface *surface)
{
surface->pending = g_object_new (META_TYPE_WAYLAND_PENDING_STATE, NULL);
}
static void

View File

@@ -31,6 +31,7 @@
#include "meta-wayland-types.h"
#include "meta-surface-actor.h"
#include "backends/meta-monitor-manager-private.h"
#include "meta-wayland-pointer-constraints.h"
typedef struct _MetaWaylandPendingState MetaWaylandPendingState;
@@ -44,6 +45,12 @@ G_DECLARE_FINAL_TYPE (MetaWaylandSurface,
G_DECLARE_DERIVABLE_TYPE (MetaWaylandSurfaceRole, meta_wayland_surface_role,
META, WAYLAND_SURFACE_ROLE, GObject);
#define META_TYPE_WAYLAND_PENDING_STATE (meta_wayland_pending_state_get_type ())
G_DECLARE_FINAL_TYPE (MetaWaylandPendingState,
meta_wayland_pending_state,
META, WAYLAND_PENDING_STATE,
GObject);
struct _MetaWaylandSurfaceRoleClass
{
GObjectClass parent_class;
@@ -92,6 +99,8 @@ G_DECLARE_FINAL_TYPE (MetaWaylandSurfaceRoleDND,
struct _MetaWaylandPendingState
{
GObject parent;
/* wl_surface.attach */
gboolean newly_attached;
MetaWaylandBuffer *buffer;
@@ -128,6 +137,8 @@ struct _MetaWaylandDragDestFuncs
const ClutterEvent *event);
void (* drop) (MetaWaylandDataDevice *data_device,
MetaWaylandSurface *surface);
void (* update) (MetaWaylandDataDevice *data_device,
MetaWaylandSurface *surface);
};
struct _MetaWaylandSurface
@@ -159,7 +170,7 @@ struct _MetaWaylandSurface
} dnd;
/* All the pending state that wl_surface.commit will apply. */
MetaWaylandPendingState pending;
MetaWaylandPendingState *pending;
/* Extension resources. */
struct wl_resource *xdg_surface;
@@ -200,13 +211,15 @@ struct _MetaWaylandSurface
* state here.
*/
gboolean synchronous;
MetaWaylandPendingState pending;
MetaWaylandPendingState *pending;
int32_t pending_x;
int32_t pending_y;
gboolean pending_pos;
GSList *pending_placement_ops;
} sub;
GList *pointer_constraints;
};
void meta_wayland_shell_init (MetaWaylandCompositor *compositor);
@@ -240,6 +253,7 @@ void meta_wayland_surface_drag_dest_motion (MetaWaylandSurface
const ClutterEvent *event);
void meta_wayland_surface_drag_dest_focus_out (MetaWaylandSurface *surface);
void meta_wayland_surface_drag_dest_drop (MetaWaylandSurface *surface);
void meta_wayland_surface_drag_dest_update (MetaWaylandSurface *surface);
void meta_wayland_surface_update_outputs (MetaWaylandSurface *surface);
@@ -250,6 +264,27 @@ void meta_wayland_surface_queue_pending_frame_callbacks (MetaWayl
void meta_wayland_surface_queue_pending_state_frame_callbacks (MetaWaylandSurface *surface,
MetaWaylandPendingState *pending);
void meta_wayland_surface_add_pointer_constraint (MetaWaylandSurface *surface,
MetaWaylandPointerConstraint *constraint);
void meta_wayland_surface_remove_pointer_constraint (MetaWaylandSurface *surface,
MetaWaylandPointerConstraint *constraint);
MetaWaylandPointerConstraint *
meta_wayland_surface_get_pointer_constraint_for_seat (MetaWaylandSurface *surface,
MetaWaylandSeat *seat);
void meta_wayland_surface_get_relative_coordinates (MetaWaylandSurface *surface,
float abs_x,
float abs_y,
float *sx,
float *sy);
void meta_wayland_surface_get_absolute_coordinates (MetaWaylandSurface *surface,
float sx,
float sy,
float *x,
float *y);
MetaWaylandSurface * meta_wayland_surface_role_get_surface (MetaWaylandSurfaceRole *role);
#endif

View File

@@ -29,6 +29,8 @@ typedef struct _MetaWaylandPointerGrabInterface MetaWaylandPointerGrabInterface;
typedef struct _MetaWaylandPopupGrab MetaWaylandPopupGrab;
typedef struct _MetaWaylandPopup MetaWaylandPopup;
typedef struct _MetaWaylandKeyboard MetaWaylandKeyboard;
typedef struct _MetaWaylandKeyboardGrab MetaWaylandKeyboardGrab;
typedef struct _MetaWaylandKeyboardGrabInterface MetaWaylandKeyboardGrabInterface;
typedef struct _MetaWaylandTouch MetaWaylandTouch;
typedef struct _MetaWaylandDragDestFuncs MetaWaylandDragDestFuncs;
typedef struct _MetaWaylandDataOffer MetaWaylandDataOffer;

View File

@@ -36,13 +36,13 @@
/* Global/master objects (version exported by wl_registry and negotiated through bind) */
#define META_WL_COMPOSITOR_VERSION 3
#define META_WL_DATA_DEVICE_MANAGER_VERSION 2
#define META_WL_DATA_DEVICE_MANAGER_VERSION 3
#define META_XDG_SHELL_VERSION 1
#define META_WL_SHELL_VERSION 1
#define META_WL_SEAT_VERSION 4
#define META_WL_SEAT_VERSION 5
#define META_WL_OUTPUT_VERSION 2
#define META_XSERVER_VERSION 1
#define META_GTK_SHELL_VERSION 2
#define META_GTK_SHELL_VERSION 3
#define META_WL_SUBCOMPOSITOR_VERSION 1
#define META_ZWP_POINTER_GESTURES_V1_VERSION 1

View File

@@ -48,14 +48,6 @@ meta_wayland_compositor_get_default (void)
return &_meta_wayland_compositor;
}
static guint32
get_time (void)
{
struct timeval tv;
gettimeofday (&tv, NULL);
return tv.tv_sec * 1000 + tv.tv_usec / 1000;
}
typedef struct
{
GSource source;
@@ -186,7 +178,7 @@ meta_wayland_compositor_paint_finished (MetaWaylandCompositor *compositor)
MetaWaylandFrameCallback *callback =
wl_container_of (compositor->frame_callbacks.next, callback, link);
wl_callback_send_done (callback->resource, get_time ());
wl_callback_send_done (callback->resource, g_get_monotonic_time () / 1000);
wl_resource_destroy (callback->resource);
}
}
@@ -336,6 +328,8 @@ meta_wayland_init (void)
meta_wayland_shell_init (compositor);
meta_wayland_pointer_gestures_init (compositor);
meta_wayland_seat_init (compositor);
meta_wayland_relative_pointer_init (compositor);
meta_wayland_pointer_constraints_init (compositor);
if (!meta_xwayland_start (&compositor->xwayland_manager, compositor->wayland_display))
g_error ("Failed to start X Wayland");

View File

@@ -30,6 +30,7 @@
#include "window-private.h"
#include "boxes-private.h"
#include "stack-tracker.h"
#include "meta-wayland-private.h"
#include "meta-wayland-surface.h"
#include "compositor/meta-surface-actor-wayland.h"
@@ -402,6 +403,11 @@ appears_focused_changed (GObject *object,
gpointer user_data)
{
MetaWindow *window = META_WINDOW (object);
MetaWaylandCompositor *wayland_compositor;
wayland_compositor = meta_wayland_compositor_get_default ();
meta_wayland_pointer_constraint_maybe_remove_for_seat (wayland_compositor->seat,
window);
/* When we're unmanaging, we remove focus from the window,
* causing this to fire. Don't do anything in that case. */
@@ -409,6 +415,8 @@ appears_focused_changed (GObject *object,
return;
surface_state_changed (window);
meta_wayland_pointer_constraint_maybe_enable_for_window (window);
}
static void

View File

@@ -108,6 +108,7 @@ enum {
ATOM_DND_ACTION_MOVE,
ATOM_DND_ACTION_COPY,
ATOM_DND_ACTION_ASK,
ATOM_DND_ACTION_PRIVATE,
N_DND_ATOMS
};
@@ -126,6 +127,7 @@ const gchar *atom_names[] = {
"XdndActionMove",
"XdndActionCopy",
"XdndActionAsk",
"XdndActionPrivate",
NULL
};
@@ -135,6 +137,33 @@ G_DEFINE_TYPE (MetaWaylandDataSourceXWayland, meta_wayland_data_source_xwayland,
META_TYPE_WAYLAND_DATA_SOURCE);
/* XDND helpers */
static Atom
action_to_atom (uint32_t action)
{
if (action & WL_DATA_DEVICE_MANAGER_DND_ACTION_COPY)
return xdnd_atoms[ATOM_DND_ACTION_COPY];
else if (action & WL_DATA_DEVICE_MANAGER_DND_ACTION_MOVE)
return xdnd_atoms[ATOM_DND_ACTION_MOVE];
else if (action & WL_DATA_DEVICE_MANAGER_DND_ACTION_ASK)
return xdnd_atoms[ATOM_DND_ACTION_ASK];
else
return None;
}
static enum wl_data_device_manager_dnd_action
atom_to_action (Atom atom)
{
if (atom == xdnd_atoms[ATOM_DND_ACTION_COPY] ||
atom == xdnd_atoms[ATOM_DND_ACTION_PRIVATE])
return WL_DATA_DEVICE_MANAGER_DND_ACTION_COPY;
else if (atom == xdnd_atoms[ATOM_DND_ACTION_MOVE])
return WL_DATA_DEVICE_MANAGER_DND_ACTION_MOVE;
else if (atom == xdnd_atoms[ATOM_DND_ACTION_ASK])
return WL_DATA_DEVICE_MANAGER_DND_ACTION_ASK;
else
return WL_DATA_DEVICE_MANAGER_DND_ACTION_NONE;
}
static void
xdnd_send_enter (MetaXWaylandSelection *selection_data,
Window dest)
@@ -217,10 +246,21 @@ xdnd_send_position (MetaXWaylandSelection *selection_data,
int x,
int y)
{
MetaWaylandCompositor *compositor = meta_wayland_compositor_get_default ();
MetaSelectionBridge *selection = &selection_data->dnd.selection;
MetaWaylandDataSource *source = compositor->seat->data_device.dnd_data_source;
Display *xdisplay = GDK_DISPLAY_XDISPLAY (gdk_display_get_default ());
uint32_t action = 0, user_action, actions;
XEvent xev = { 0 };
user_action = meta_wayland_data_source_get_user_action (source);
actions = meta_wayland_data_source_get_actions (source);
if (user_action & actions)
action = user_action;
if (!action)
action = actions;
xev.xclient.type = ClientMessage;
xev.xclient.message_type = xdnd_atoms[ATOM_DND_POSITION];
xev.xclient.format = 32;
@@ -230,7 +270,7 @@ xdnd_send_position (MetaXWaylandSelection *selection_data,
xev.xclient.data.l[1] = 0;
xev.xclient.data.l[2] = (x << 16) | y;
xev.xclient.data.l[3] = time;
xev.xclient.data.l[4] = xdnd_atoms[ATOM_DND_ACTION_COPY];
xev.xclient.data.l[4] = action_to_atom (action);
XSendEvent (xdisplay, dest, False, NoEventMask, &xev);
}
@@ -262,6 +302,8 @@ xdnd_send_finished (MetaXWaylandSelection *selection_data,
{
MetaDndBridge *selection = &selection_data->dnd;
Display *xdisplay = GDK_DISPLAY_XDISPLAY (gdk_display_get_default ());
MetaWaylandDataSource *source = selection_data->dnd.selection.source;
uint32_t action = 0;
XEvent xev = { 0 };
xev.xclient.type = ClientMessage;
@@ -273,8 +315,9 @@ xdnd_send_finished (MetaXWaylandSelection *selection_data,
if (accepted)
{
action = meta_wayland_data_source_get_current_action (source);
xev.xclient.data.l[1] = 1; /* Drop successful */
xev.xclient.data.l[2] = xdnd_atoms[ATOM_DND_ACTION_COPY];
xev.xclient.data.l[2] = action_to_atom (action);
}
XSendEvent (xdisplay, dest, False, NoEventMask, &xev);
@@ -283,7 +326,7 @@ xdnd_send_finished (MetaXWaylandSelection *selection_data,
static void
xdnd_send_status (MetaXWaylandSelection *selection_data,
Window dest,
gboolean accepted)
uint32_t action)
{
MetaDndBridge *selection = &selection_data->dnd;
Display *xdisplay = GDK_DISPLAY_XDISPLAY (gdk_display_get_default ());
@@ -296,12 +339,10 @@ xdnd_send_status (MetaXWaylandSelection *selection_data,
xev.xclient.data.l[0] = selection->dnd_window;
xev.xclient.data.l[1] = 1 << 1; /* Bit 2: dest wants XdndPosition messages */
xev.xclient.data.l[4] = action_to_atom (action);
if (accepted)
{
xev.xclient.data.l[1] |= 1 << 0; /* Bit 1: dest accepts the drop */
xev.xclient.data.l[4] = xdnd_atoms[ATOM_DND_ACTION_COPY];
}
if (xev.xclient.data.l[4])
xev.xclient.data.l[1] |= 1 << 0; /* Bit 1: dest accepts the drop */
XSendEvent (xdisplay, dest, False, NoEventMask, &xev);
}
@@ -387,6 +428,32 @@ x11_selection_data_free (X11SelectionData *data)
g_slice_free (X11SelectionData, data);
}
static void
x11_selection_data_send_finished (MetaSelectionBridge *selection,
gboolean success)
{
uint32_t action = meta_wayland_data_source_get_current_action (selection->source);
if (!selection->x11_selection)
return;
if (success && action == WL_DATA_DEVICE_MANAGER_DND_ACTION_MOVE)
{
Display *xdisplay = GDK_DISPLAY_XDISPLAY (gdk_display_get_default ());
/* Request data deletion on the drag source */
XConvertSelection (xdisplay,
selection->selection_atom,
gdk_x11_get_xatom_by_name ("DELETE"),
gdk_x11_get_xatom_by_name ("_META_SELECTION"),
selection->window,
CurrentTime);
}
xdnd_send_finished (selection->x11_selection->selection_data,
selection->owner, success);
}
static void
x11_selection_data_finish (MetaSelectionBridge *selection,
gboolean success)
@@ -395,13 +462,18 @@ x11_selection_data_finish (MetaSelectionBridge *selection,
return;
if (selection == &selection->x11_selection->selection_data->dnd.selection)
xdnd_send_finished (selection->x11_selection->selection_data,
selection->owner, success);
x11_selection_data_send_finished (selection, success);
g_clear_pointer (&selection->x11_selection,
(GDestroyNotify) x11_selection_data_free);
}
static void
x11_selection_data_close (X11SelectionData *data)
{
g_output_stream_close (data->stream, data->cancellable, NULL);
}
static void
x11_data_write_cb (GObject *object,
GAsyncResult *res,
@@ -435,6 +507,7 @@ x11_data_write_cb (GObject *object,
}
else
{
x11_selection_data_close (selection->x11_selection);
x11_selection_data_finish (selection, success);
}
}
@@ -693,6 +766,7 @@ meta_xwayland_selection_get_incr_chunk (MetaWaylandCompositor *compositor,
else
{
/* Transfer has completed */
x11_selection_data_close (selection->x11_selection);
x11_selection_data_finish (selection, TRUE);
}
@@ -741,11 +815,15 @@ meta_x11_source_target (MetaWaylandDataSource *source,
MetaWaylandDataSourceXWayland *source_xwayland =
META_WAYLAND_DATA_SOURCE_XWAYLAND (source);
MetaSelectionBridge *selection = source_xwayland->selection;
uint32_t action = 0;
if (selection->selection_atom == xdnd_atoms[ATOM_DND_SELECTION])
{
if (mime_type)
action = meta_wayland_data_source_get_current_action (source);
xdnd_send_status (compositor->xwayland_manager.selection_data,
selection->owner, mime_type != NULL);
selection->owner, action);
}
}
@@ -756,10 +834,46 @@ meta_x11_source_cancel (MetaWaylandDataSource *source)
META_WAYLAND_DATA_SOURCE_XWAYLAND (source);
MetaSelectionBridge *selection = source_xwayland->selection;
x11_selection_data_send_finished (selection, FALSE);
g_clear_pointer (&selection->x11_selection,
(GDestroyNotify) x11_selection_data_free);
}
static void
meta_x11_source_action (MetaWaylandDataSource *source,
uint32_t action)
{
MetaWaylandCompositor *compositor = meta_wayland_compositor_get_default ();
MetaWaylandDataSourceXWayland *source_xwayland =
META_WAYLAND_DATA_SOURCE_XWAYLAND (source);
MetaSelectionBridge *selection = source_xwayland->selection;
if (selection->selection_atom == xdnd_atoms[ATOM_DND_SELECTION])
{
if (!meta_wayland_data_source_has_target (source))
action = 0;
xdnd_send_status (compositor->xwayland_manager.selection_data,
selection->owner, action);
}
}
static void
meta_x11_source_drop_performed (MetaWaylandDataSource *source)
{
}
static void
meta_x11_source_drag_finished (MetaWaylandDataSource *source)
{
MetaWaylandDataSourceXWayland *source_xwayland =
META_WAYLAND_DATA_SOURCE_XWAYLAND (source);
MetaSelectionBridge *selection = source_xwayland->selection;
if (selection->x11_selection)
x11_selection_data_send_finished (selection, TRUE);
}
static void
meta_wayland_data_source_xwayland_init (MetaWaylandDataSourceXWayland *source_xwayland)
{
@@ -774,6 +888,9 @@ meta_wayland_data_source_xwayland_class_init (MetaWaylandDataSourceXWaylandClass
data_source_class->send = meta_x11_source_send;
data_source_class->target = meta_x11_source_target;
data_source_class->cancel = meta_x11_source_cancel;
data_source_class->action = meta_x11_source_action;
data_source_class->drop_performed = meta_x11_source_drop_performed;
data_source_class->drag_finished = meta_x11_source_drag_finished;
}
static MetaWaylandDataSource *
@@ -837,11 +954,27 @@ meta_x11_drag_dest_drop (MetaWaylandDataDevice *data_device,
meta_display_get_current_time_roundtrip (meta_get_display ()));
}
static void
meta_x11_drag_dest_update (MetaWaylandDataDevice *data_device,
MetaWaylandSurface *surface)
{
MetaWaylandCompositor *compositor = meta_wayland_compositor_get_default ();
MetaWaylandSeat *seat = compositor->seat;
ClutterPoint pos;
clutter_input_device_get_coords (seat->pointer.device, NULL, &pos);
xdnd_send_position (compositor->xwayland_manager.selection_data,
compositor->xwayland_manager.selection_data->dnd.dnd_dest,
clutter_get_current_event_time (),
pos.x, pos.y);
}
static const MetaWaylandDragDestFuncs meta_x11_drag_dest_funcs = {
meta_x11_drag_dest_focus_in,
meta_x11_drag_dest_focus_out,
meta_x11_drag_dest_motion,
meta_x11_drag_dest_drop
meta_x11_drag_dest_drop,
meta_x11_drag_dest_update
};
const MetaWaylandDragDestFuncs *
@@ -1135,6 +1268,10 @@ meta_xwayland_selection_handle_selection_request (MetaWaylandCompositor *composi
selection->timestamp);
reply_selection_request (event, TRUE);
}
else if (data_source && event->target == gdk_x11_get_xatom_by_name ("DELETE"))
{
reply_selection_request (event, TRUE);
}
else
{
if (data_source &&
@@ -1257,6 +1394,7 @@ meta_xwayland_selection_handle_client_message (MetaWaylandCompositor *compositor
if (event->window == dnd->selection.window)
{
MetaWaylandDataSource *data_source;
uint32_t action = 0;
data_source = compositor->seat->data_device.dnd_data_source;
@@ -1269,6 +1407,11 @@ meta_xwayland_selection_handle_client_message (MetaWaylandCompositor *compositor
meta_wayland_data_source_set_has_target (data_source,
(event->data.l[1] & 1) != 0);
/* data.l[4] contains the action atom */
if (event->data.l[4])
action = atom_to_action ((Atom) event->data.l[4]);
meta_wayland_data_source_set_current_action (data_source, action);
return TRUE;
}
else if (event->message_type == xdnd_atoms[ATOM_DND_FINISHED])
@@ -1277,8 +1420,7 @@ meta_xwayland_selection_handle_client_message (MetaWaylandCompositor *compositor
if (compositor->seat->data_device.current_grab)
return FALSE;
meta_wayland_data_device_set_dnd_source (&compositor->seat->data_device,
NULL);
meta_wayland_data_source_notify_finish (data_source);
return TRUE;
}
}
@@ -1290,7 +1432,8 @@ meta_xwayland_selection_handle_client_message (MetaWaylandCompositor *compositor
MetaWaylandDragGrab *drag_grab = compositor->seat->data_device.current_grab;
MetaWaylandSurface *drag_focus = meta_wayland_drag_grab_get_focus (drag_grab);
if (!drag_focus)
if (!drag_focus &&
event->message_type != xdnd_atoms[ATOM_DND_ENTER])
return FALSE;
if (event->message_type == xdnd_atoms[ATOM_DND_ENTER])
@@ -1338,6 +1481,7 @@ meta_xwayland_selection_handle_client_message (MetaWaylandCompositor *compositor
{
ClutterEvent *motion;
ClutterPoint pos;
uint32_t action = 0;
motion = clutter_event_new (CLUTTER_MOTION);
clutter_input_device_get_coords (seat->pointer.device, NULL, &pos);
@@ -1346,11 +1490,13 @@ meta_xwayland_selection_handle_client_message (MetaWaylandCompositor *compositor
clutter_event_set_source_device (motion, seat->pointer.device);
clutter_event_set_time (motion, dnd->last_motion_time);
action = atom_to_action ((Atom) event->data.l[4]);
meta_wayland_data_source_set_actions (dnd->selection.source, action);
meta_wayland_surface_drag_dest_motion (drag_focus, motion);
xdnd_send_status (compositor->xwayland_manager.selection_data,
(Window) event->data.l[0],
meta_wayland_data_source_has_target (
dnd->selection.source));
meta_wayland_data_source_get_current_action (dnd->selection.source));
clutter_event_free (motion);
return TRUE;

View File

@@ -1,6 +1,6 @@
<protocol name="gtk">
<interface name="gtk_shell" version="2">
<interface name="gtk_shell" version="3">
<description summary="gtk specific extensions">
gtk_shell is a protocol extension providing additional features for
clients implementing it. It is not backward compatible, and a client must
@@ -23,9 +23,13 @@
<arg name="gtk_surface" type="new_id" interface="gtk_surface"/>
<arg name="surface" type="object" interface="wl_surface"/>
</request>
<request name="set_startup_id" since="3">
<arg name="startup_id" type="string" allow-null="true"/>
</request>
</interface>
<interface name="gtk_surface" version="2">
<interface name="gtk_surface" version="3">
<request name="set_dbus_properties">
<arg name="application_id" type="string" allow-null="true"/>
<arg name="app_menu_path" type="string" allow-null="true"/>

View File

@@ -1671,13 +1671,12 @@ meta_display_handle_xevent (MetaDisplay *display,
meta_spew_event_print (display, event);
#endif
#ifdef HAVE_STARTUP_NOTIFICATION
if (sn_display_process_event (display->sn_display, event))
if (meta_startup_notification_handle_xevent (display->startup_notification,
event))
{
bypass_gtk = bypass_compositor = TRUE;
goto out;
}
#endif
#ifdef HAVE_WAYLAND
if (meta_is_wayland_compositor () &&

View File

@@ -1732,9 +1732,9 @@ meta_window_x11_update_input_region (MetaWindow *window)
region = cairo_region_create ();
}
else if (n_rects == 1 &&
(rects[0].x == 0 ||
rects[0].y == 0 ||
rects[0].width == priv->client_rect.width ||
(rects[0].x == 0 &&
rects[0].y == 0 &&
rects[0].width == priv->client_rect.width &&
rects[0].height == priv->client_rect.height))
{
/* This is the bounding region case. Keep the
@@ -1847,6 +1847,12 @@ meta_window_x11_update_shape_region (MetaWindow *window)
* this is simply the client area.
*/
cairo_region_intersect_rectangle (region, &client_area);
/* Some applications might explicitly set their bounding region
* to the client area. Detect these cases, and throw out the
* bounding region in this case. */
if (cairo_region_contains_rectangle (region, &client_area) == CAIRO_REGION_OVERLAP_IN)
g_clear_pointer (&region, cairo_region_destroy);
}
meta_window_set_shape_region (window, region);

View File

@@ -194,6 +194,7 @@ async_get_property_finish (xcb_connection_t *xcb_conn,
{
xcb_get_property_reply_t *reply;
xcb_generic_error_t *error;
int length;
reply = xcb_get_property_reply (xcb_conn, cookie, &error);
if (error)
@@ -209,8 +210,15 @@ async_get_property_finish (xcb_connection_t *xcb_conn,
results->prop = NULL;
if (results->type != None)
results->prop = g_memdup (xcb_get_property_value (reply),
xcb_get_property_value_length (reply));
{
length = xcb_get_property_value_length (reply);
/* Leave room for a trailing '\0' since xcb doesn't return null-terminated
* strings
*/
results->prop = g_malloc (length + 1);
memcpy (results->prop, xcb_get_property_value (reply), length);
results->prop[length] = '\0';
}
free (reply);
return (results->prop != NULL);