Compare commits

...

60 Commits

Author SHA1 Message Date
Colin Walters
a1790fb6a8 configure: Increment version
This must have been unpushed after the release.
2010-12-23 11:58:43 -05:00
Florian Müllner
cbce4fd39d ui: Adapt to GDK API changes
Some API which was only meaningful on X11 was moved to the X11
backend and renamed accordingly, so adapt to those changes.

https://bugzilla.gnome.org/show_bug.cgi?id=637802
2010-12-22 13:39:36 -05:00
Nguyễn Thái Ngọc Duy
93c0620151 po/vi.po: merge translation updates from metacity 2010-12-19 17:58:05 +07:00
Emmanuele Bassi
2faf56947b Use GdkDeviceManager to get the core pointer
Commit 2c8c1c6df49 in gtk+ removed gdk_display_get_core_pointer().

The equivalent functionality can be achieved by using the
GdkDeviceManager to retrieve the client pointer device.
2010-12-17 17:12:01 +00:00
Florian Müllner
53777b133b ui: Disable multidevice support in GDK
GDK now uses XI2 by default, which conflicts with the X calls done
in mutter. Enforce the previous behavior.
2010-12-15 18:11:13 +01:00
Florian Müllner
594a69317a tiling: Reset maximized tile mode when unmaximizing
Maximized tiled windows end up with an inconsistent tile mode when
unmaximized by other means than dragging the window free (e.g.
using the unmaximize button or double clicking the title bar), so
reset the tile mode when unmaximizing.

This is not a problem for side-by-side tiling, as there are no
alternatives to dragging the window free.
2010-12-10 17:30:18 +01:00
Owen W. Taylor
3f9c375f1c MetaRegionIterator: avoid reading off end of rectangles array
Fix an off-by-one error in the check for "can we peek ahead to the next
rectangle".

https://bugzilla.gnome.org/show_bug.cgi?id=636491
2010-12-06 12:00:30 -05:00
Colin Walters
544c8edd9e theme: Handle new GTK+ states 2010-12-04 17:33:01 -05:00
Florian Müllner
654bd15319 tiling: Do not restore maximized tile state when unmaximizing
When a tiled window is maximized (e.g. by clicking the title bar
button), unmaximizing the window restores the tiled state. While
this is reasonable for side-by-side tiling, it is confusing for
"maximize" tiled windows, as unmaximization has no visible effect.

Change unmaximize to only restore the tiled state of side-by-side
tiled windows.
2010-12-04 21:35:15 +01:00
Florian Müllner
b85171007e window: Add a missing break
When adding maximize tiling, the cleaning of the tiled flag was
refactored to use a switch statement - a break was missed in the
process.
2010-12-04 21:35:15 +01:00
Matej Urbančič
f6ec33fa0c Updated Slovenian translation 2010-12-03 20:19:53 +01:00
Florian Müllner
7d0ff87cbe tiling: Fix "maximize" tile with no chrome at the top
The original patch triggered "maximize" when the window was dragged
to the top, so that the pointer was below or on the monitor edge and
above the work area's top.

If there's no chrome at the top of the monitor, so that monitor edge
and work area top fall together, the pointer cannot be moved above
the work area's top, so tiling was not triggered.
2010-12-03 02:45:41 +01:00
Benjamin Otte
8994e621f7 Replace some GDK X11 calls with future-proof ones
GTK is about to clean up its code and remove duplicate macros and
GdkDrawable usage. To prepare for that landing, we use the future-safe
versions of the same calls.

https://bugzilla.gnome.org/show_bug.cgi?id=636302
2010-12-03 00:17:42 +01:00
Benjamin Otte
d746591894 ui: Port testgradient example to GTK3
https://bugzilla.gnome.org/show_bug.cgi?id=636301
2010-12-03 00:17:42 +01:00
Florian Müllner
161c1088f9 tiling: disable "shake loose" feature when edge tiling
The old behavior of being able to shake loose a maximized window
overlaps with and is for the most part superceded by top edge tiling.

This commit changes the code to only enable shake loose behavior
when edge tiling is disabled.

https://bugzilla.gnome.org/show_bug.cgi?id=630548
2010-12-02 17:42:14 -05:00
Ray Strode
07c0471902 tiling: Add new "maximized" tile
In addition to the existing side-by-side tiling modes, this commit
adds a new "maximize" tiling mode.  It allows the user to maximize
their windows (in other words, tile with the edge panels) by dragging
their window to the top edge of the monitor.

https://bugzilla.gnome.org/show_bug.cgi?id=630548
2010-12-02 17:11:11 -05:00
Ray Strode
5f4ee1b6e7 tiling: use META_WINDOW_TILED_SIDE_BY_SIDE in state check
The meta_window_handle_mouse_grab_op_event function ensures
the tile_mode variable is in a consistent state after a drag
op is finished.

The way this is current done is:

if (!window->maximized_vertically &&
    window->tile_mode != META_TILE_NONE)
  window->tile_mode = META_TILE_NONE;

While valid, it doesn't "read" as well as using the
META_WINDOW_TILED_SIDE_BY_SIDE macro, since the macro is specifically
about side-by-side tiling.

This commit just changes things to use the macro and to not bother
checking the tile mode (since if we just reset it anyway, then it doesn't
matter if the value is right or wrong to begin with).

https://bugzilla.gnome.org/show_bug.cgi?id=630548
2010-12-02 16:35:23 -05:00
Ray Strode
aa3a4a48e4 tiling: add side_by_side suffix to tile code
The meta_window_can_tile function and META_WINDOW_TILED
macro are pretty side-by-side tiling specific, so
rename them.

https://bugzilla.gnome.org/show_bug.cgi?id=630548
2010-12-02 16:35:23 -05:00
Ray Strode
0d27c9600f tiling: rename side-by-side tiling to edge tiling
Currently, the new tiling feature, supports side-by-side, horizontal
tiling when dragging windows to one of the vertical edges of a monitor.

Other types of tiling (on other monitor edges) are potentially useful,
though.

This commit renames the preference from side_by_side_tiling to
edge_tiling.

https://bugzilla.gnome.org/show_bug.cgi?id=630548
2010-12-02 16:35:23 -05:00
Milan Bouchet-Valat
1986b20499 Add (out) annotation and documentation to meta_window_get_outer_rectangle()
GObject introspection cannot detect that rect is an (out) parameter.
Also add a meaningful documentation for humans like us.
2010-12-02 00:06:42 +01:00
Owen W. Taylor
e7a10b0d6a Bump version to 2.91.3 2010-11-29 16:04:46 -05:00
Florian Müllner
6b98644c58 workspace: Consider text direction when switching
A direction parameter is passed to meta_compositor_switch_workspace(),
to indicate the direction of the switch depending on the workspace
layout.
In contrast to the switcher popup, this parameter does not take the
text direction of the locale into account. Change this, so that the
workspace switching animations move in the correct direction.

https://bugzilla.gnome.org/show_bug.cgi?id=636083
2010-11-29 21:42:58 +01:00
Dan Williams
5397335ae8 Fix builds with clutter < 1.5.2 2010-11-22 22:00:02 -06:00
Diego Escalante Urrelo
0b6d6e517d configure.in: it's git, not Subversion
Bug #635493
2010-11-22 20:04:22 -05:00
Owen W. Taylor
b031543f5a Fix updating overlay key keycode when keymap changes
overlay_key_combo needs the same treatment as other keycodes on a
change - we should always recompute it if we have a keysym not
a keycode, and not only if the keycode hasn't already been
computed.
2010-11-22 16:10:34 -05:00
Owen W. Taylor
c6be05f9d7 Unify keymap-reloading code branches
Simplify the keymap loading logic by unifying the different
branches; in the reorganization this patch fixes a bug where when
we got a MappingKeyboard event we wouldn't update virtual modifiers
correctly.

Based on a patch by Thomas Thurman <tthurman@gnome.org>

https://bugzilla.gnome.org/show_bug.cgi?id=565540
2010-11-22 16:02:03 -05:00
Derek Poon
cb88e0d052 Update keybindings when XKB keyboard layout changes
* Select for XKB keyboard notification events explicitly; since GTK+
  has selected for XKB events, delivery of old-school MappingNotify
  events is disabled.

* Fix a bug where once a keycode was loaded for a key binding,
  it would never be reassigned; we want to laod new keycodes for
  all bindings that have a key symbol rather than a fixed
  keycode.

[ With fixes from Owen W. Taylor <otaylor@fishsoup.net> ]

https://bugzilla.gnome.org/show_bug.cgi?id=565540
2010-11-22 16:02:03 -05:00
Owen W. Taylor
54e82daae2 configure.ac: move CFLAGS modifications after all tests
-Werror in CFLAGS was causing the check for GNU gettext in
AM_GNU_GLIB_GETTEXT to fail, resulting in .mo files in $libdir.

https://bugzilla.gnome.org/show_bug.cgi?id=635528
2010-11-22 10:49:55 -05:00
Adel Gadllah
1394c566eb [MetaWindowActor] Fix crash in shadow shape creation
Protect against shape_region or bounding_region being NULL in check_needs_shadow.

This can happen for short lived windows and result into a crash.

https://bugzilla.gnome.org/show_bug.cgi?id=635421
2010-11-21 19:57:42 +01:00
Yaron Shahrabani
3183d954a0 Updated Hebrew translation. 2010-11-21 12:58:12 +02:00
Kjartan Maraas
67b89e5c86 Updated Norwegian bokmål translation 2010-11-20 18:40:39 +01:00
Gheyret T.Kenji
c88c94886b Added UG translation 2010-11-20 12:06:01 +01:00
Fran Diéguez
c1f6902cb9 Updated Galician translations 2010-11-20 01:24:44 +01:00
Owen W. Taylor
8817e68926 Use COGL_MATERIAL_WRAP_MODE_* defines not COGL_PIPELINE_*
Since we aren't depending on Clutter 1.5 or using the new
CoglPipeline name elsewhere, we need to stick to the old
COGL_MATERIAL_WRAP_MODE_* names, which are provided with
compatibility defines in Clutter 1.4.

Pointed out by Rico Tzschichholz
2010-11-19 14:41:44 -05:00
Jorge González
b1868fb213 Updated Spanish translation 2010-11-19 13:46:08 +01:00
Owen W. Taylor
6260814285 Turn off background repeats for a full-size image
If we have repeats on for a full-sized image, then if the background
is displayed scaled (for example, in a desktop preview mode) then we
can get artifacts along the edge of the background where the repeat
of the opposite edge is blended in by bilinear scaling. So turn off
repeats when the screen and background image sizes match.

https://bugzilla.gnome.org/show_bug.cgi?id=634833
2010-11-18 13:58:37 -05:00
Owen W. Taylor
07e6c5aac2 Draw the root window background
Add code to track and draw the root window background. The advantage of doing
it here as compared to in a plugin is that we can use the visiblity smarts
of MetaWindowGroup to optimize out drawing the background when obscured.

If handling other than tracking the _XROOTPMAP_ID property is desired in the
future, more functionality like setting the background from a file or doing
cross-fades can be added.

The new background actor is exposed to plugins via meta_plugin_get_background_actor()
similar to other exposed actors to allow cloning the background for use in
other displays. The actual class is not installed for public consumption at
the moment since it has no useful methods.

https://bugzilla.gnome.org/show_bug.cgi?id=634833
2010-11-18 13:58:37 -05:00
Owen W. Taylor
0477a3066d Refactor "texture material" creation from MetaShadowFactory
Create new cogl-utils.[ch] and move a helper function from
MetaShadowFactory there as meta_create_texture_material(); this
allows us to create single-layer materials from different parts of
Mutter and have them share the same template material.

Also expose a function for creating a 1x1 texture of a given
color meta_create_color_texture_4ub().

https://bugzilla.gnome.org/show_bug.cgi?id=634833
2010-11-18 13:58:37 -05:00
Owen W. Taylor
59639909b1 MetaWindowGroup: further optimize paints by using current scissor
When in a partial stage paint, we can combine that with the visibility
information in MetaWindowGroup to further eliminate unneeded drawing.

Since there is no current Clutter API to access the current clip,
drop to using GL directly.

https://bugzilla.gnome.org/show_bug.cgi?id=634779
2010-11-18 13:58:36 -05:00
Adel Gadllah
64c37852b1 meta-actor-window: Use G_UNLIKELY for TFP check
This is just a microptimization, as we pretty much always use
TFP (and do the check every time we set a pixmap),
we can let gcc generate better code here.

https://bugzilla.gnome.org/show_bug.cgi?id=633002
2010-11-18 18:51:04 +01:00
Owen W. Taylor
bac668d287 Optimize out restacks that don't change actor order
For various reasons (mostly the stack tracker correctly predicting the
stacking order before getting events, but also because of the processing
that the compositor does to get the actor stacking order) the compositor
can be told to sync the stack when it has nothing to do. Detect this
at the last moment before actually telling Clutter to restack to avoid
triggering unnecessary redraws.

https://bugzilla.gnome.org/show_bug.cgi?id=634771
2010-11-18 10:07:38 -05:00
Owen W. Taylor
f372fa29b2 MetaStackTracker: Avoid queueing resync for obvious no-ops
Since we can't distinguish a ConfigureEvent that indicates a raise
from a ConfigureEvent that indicates a move, we get lots of
STACK_OP_RAISE_ABOVE events for windows that are already in the
right place in the stacking order. Avoid queueing a restack in that
case.

https://bugzilla.gnome.org/show_bug.cgi?id=634771
2010-11-18 09:54:44 -05:00
Owen W. Taylor
8b24711bba Omit shadows for fullscreen and maximized windows
Fullscreen and maximized windows never have visible shadows - the only
case where we would ever see them is if they bleed onto an adjacent
monitor and that looks bad.

It's small performance win to avoid computing them, and this also avoids
painting the top shadow for all maximized windows in GNOME Shell - since
the top panel isn't a X window, it doesn't factor into the computation
of what parts of windows are visible and maximized windows are computed
as having a top shadow.

https://bugzilla.gnome.org/show_bug.cgi?id=592382
2010-11-18 09:47:58 -05:00
Owen W. Taylor
d4c28fc5f5 Add meta_window_get_maximized() and meta_window_is_fullscreen()
These functions duplicate existing properties; they are added for
convenience and to avoid the GObject property code on some
performance critical painting paths.

https://bugzilla.gnome.org/show_bug.cgi?id=592382
2010-11-18 09:47:58 -05:00
Owen W. Taylor
ca5f2ac3ec Implement more accurate clipping of obscured shadows
Instead of making optimizing obscured shadows an all-or-none operation,
pass the clip region to meta_shadow_paint() and only paint the 9-slices
that are at least partially visible.

https://bugzilla.gnome.org/show_bug.cgi?id=592382
2010-11-18 09:47:58 -05:00
Owen W. Taylor
15f9590427 Report a correct paint volume for shadowed windows
Since we paint shadows directly now rather than using a child actor
in the ClutterGroup, we need to implement get_paint_volume() for
Clutter 1.5.

https://bugzilla.gnome.org/show_bug.cgi?id=592382
2010-11-18 09:47:58 -05:00
Owen W. Taylor
21a246eb42 Use a template material for shadows
To avoid unnecessary shader recompilation, use a root template material
for all shadows.

https://bugzilla.gnome.org/show_bug.cgi?id=592382
2010-11-18 09:47:58 -05:00
Owen W. Taylor
1bbaec81ce Make window shadows globally configurable
Instead of setting shadow parameters on individual windows, add the
idea of a "shadow class". Windows have default shadow classes based
on their frame and window type, which can be overriden by setting
the shadow-class property.

Each shadow class has separably configurable parameters for the
focused and unfocused state. New shadow classes can be defined with
arbitrary names.

https://bugzilla.gnome.org/show_bug.cgi?id=592382
2010-11-18 09:47:58 -05:00
Owen W. Taylor
7952feb48b Export meta_frame_type_to_string()
Frame types will form the bases of shadow classes, which are strings,
so export the to-string function so that we can do the conversion
uniformly.

https://bugzilla.gnome.org/show_bug.cgi?id=592382
2010-11-18 09:47:57 -05:00
Owen W. Taylor
52aebdf223 Add meta_window_get_frame_type()
Add a public function to get the frame type for a window; the
code is refactored from existing code in core.c.

https://bugzilla.gnome.org/show_bug.cgi?id=592382
2010-11-18 09:47:57 -05:00
Owen W. Taylor
6b16604c26 Export meta_window_appears_focused()
Move meta_window_appears_focused() into the public window.h so
we can use it to change the shadow type.

https://bugzilla.gnome.org/show_bug.cgi?id=592382
2010-11-18 09:47:57 -05:00
Owen W. Taylor
3c4d52732e Make MetaShadowFactory public
The basic MetaShadowFactory type is moved to a public header, while
the functions to fetch and paint shadows are kept private.
The public object will be used for configuration of shadows by
plugins.

https://bugzilla.gnome.org/show_bug.cgi?id=592382
2010-11-18 09:47:57 -05:00
Owen W. Taylor
d56d267f7d MetaShadowFactory: convert to a GObject
https://bugzilla.gnome.org/show_bug.cgi?id=592382
2010-11-18 09:47:57 -05:00
Owen W. Taylor
8eb31944a5 MetaShadowFactory: Add the ability to top-fade a shadow
For attached modal dialogs, we want the shadow to fade out at the top
as if the window was glued to the parent at the top. Add a
shadow-top-fade property to MetaWindowActor and the corresponding
parameter to meta_shadow_factory_get_shadow().

The internal implementation of MetaShadow is adjusted to work
in terms of an "inner border" and "outer border" instead of doing
the calculations in terms of an aggregate border and the spread
of the shadow. The old way of doing things gets clumsy when the
top_fade distance is added in as well.

https://bugzilla.gnome.org/show_bug.cgi?id=592382
2010-11-18 09:47:57 -05:00
Owen W. Taylor
ed2fbcd13a Add frame type for attached modal dialogs
Add a new frame type META_FRAME_TYPE_ATTACHED which is used for
attached modal dialogs.

The theme format version is bumped to 3.2, and attached windows
can have borders defined in a metacity-theme-3.xml as:

 <window version=">= 3.2" type="attached" style_set="[name]"/>

If no style is defined for "attached", drawing will fall back
to the "border" type.

https://bugzilla.gnome.org/show_bug.cgi?id=592382
2010-11-18 09:47:57 -05:00
Owen W. Taylor
9f4942e9a7 Make shadows pretty and configurable
The current shadow code just uses a single fixed texture (the Gaussian
blur of a rectangle with a fixed blur radius) for drawing all window
shadows. This patch adds the ability

* Implement efficient blurring of arbitrary regions by approximating
  a Gaussian blur with multiple box blurs.

* Detect when multiple windows can use the same shadow texture by
  converting their shape into a size-invariant MetaWindowShape.

* Add properties shadow-radius, shadow-x-offset, shadow-y-offset,
  shadow-opacity to allow the shadow for a window to be configured.

* Add meta_window_actor_paint() and draw the shadow directly
  from there rather than using a child actor.

* Remove TidyTextureFrame, which is no longer used

https://bugzilla.gnome.org/show_bug.cgi?id=592382
2010-11-18 09:47:56 -05:00
Gheyret T.Kenji
982a10ac44 Added UG translation 2010-11-14 00:06:42 +01:00
Gheyret T.Kenji
e59a9872b4 Added UG translation 2010-11-13 22:49:22 +01:00
Javier Jardón
9aedd32e01 Use gtk_button_box_new() instead gtk_[h|v]button_box_new() 2010-11-11 01:25:35 +01:00
Khaled Hosny
dd0ca4dd60 Updated Arabic translation 2010-11-09 03:40:15 +02:00
58 changed files with 8560 additions and 3213 deletions

View File

@@ -2,7 +2,7 @@ AC_PREREQ(2.50)
m4_define([mutter_major_version], [2])
m4_define([mutter_minor_version], [91])
m4_define([mutter_micro_version], [2])
m4_define([mutter_micro_version], [4])
m4_define([mutter_version],
[mutter_major_version.mutter_minor_version.mutter_micro_version])
@@ -61,72 +61,6 @@ AC_CHECK_SIZEOF(__int64)
## byte order
AC_C_BIGENDIAN
#### Warnings
# Stay command-line compatible with the gnome-common configure option. Here
# minimum/yes/maximum are the same, however.
AC_ARG_ENABLE(compile_warnings,
AS_HELP_STRING([--enable-compile-warnings=@<:@no/minimum/yes/maximum/error@:>@],[Turn on compiler warnings]),,
enable_compile_warnings=error)
changequote(,)dnl
if test "$enable_compile_warnings" != no ; then
if test "x$GCC" = "xyes"; then
case " $CFLAGS " in
*[\ \ ]-Wall[\ \ ]*) ;;
*) CFLAGS="$CFLAGS -Wall" ;;
esac
# case " $CFLAGS " in
# *[\ \ ]-Wshadow[\ \ ]*) ;;
# *) CFLAGS="$CFLAGS -Wshadow" ;;
# esac
case " $CFLAGS " in
*[\ \ ]-Wchar-subscripts[\ \ ]*) ;;
*) CFLAGS="$CFLAGS -Wchar-subscripts" ;;
esac
case " $CFLAGS " in
*[\ \ ]-Wmissing-declarations[\ \ ]*) ;;
*) CFLAGS="$CFLAGS -Wmissing-declarations" ;;
esac
case " $CFLAGS " in
*[\ \ ]-Wmissing-prototypes[\ \ ]*) ;;
*) CFLAGS="$CFLAGS -Wmissing-prototypes" ;;
esac
case " $CFLAGS " in
*[\ \ ]-Wnested-externs[\ \ ]*) ;;
*) CFLAGS="$CFLAGS -Wnested-externs" ;;
esac
case " $CFLAGS " in
*[\ \ ]-Wpointer-arith[\ \ ]*) ;;
*) CFLAGS="$CFLAGS -Wpointer-arith" ;;
esac
case " $CFLAGS " in
*[\ \ ]-Wcast-align[\ \ ]*) ;;
*) CFLAGS="$CFLAGS -Wcast-align" ;;
esac
case " $CFLAGS " in
*[\ \ ]-Wsign-compare[\ \ ]*) ;;
*) CFLAGS="$CFLAGS -Wsign-compare" ;;
esac
if test "$enable_compile_warnings" = error; then
case " $CFLAGS " in
*[\ \ ]-Werror[\ \ ]*) ;;
*) CFLAGS="$CFLAGS -Werror" ;;
esac
fi
fi
fi
changequote([,])dnl
GTK_MIN_VERSION=2.90.7
CANBERRA_GTK=libcanberra-gtk3
CANBERRA_GTK_VERSION=0.26
@@ -518,6 +452,72 @@ AM_PATH_PYTHON([2.5])
# Use gnome-doc-utils:
GNOME_DOC_INIT([0.8.0])
#### Warnings (last since -Werror can disturb other tests)
# Stay command-line compatible with the gnome-common configure option. Here
# minimum/yes/maximum are the same, however.
AC_ARG_ENABLE(compile_warnings,
AS_HELP_STRING([--enable-compile-warnings=@<:@no/minimum/yes/maximum/error@:>@],[Turn on compiler warnings]),,
enable_compile_warnings=error)
changequote(,)dnl
if test "$enable_compile_warnings" != no ; then
if test "x$GCC" = "xyes"; then
case " $CFLAGS " in
*[\ \ ]-Wall[\ \ ]*) ;;
*) CFLAGS="$CFLAGS -Wall" ;;
esac
# case " $CFLAGS " in
# *[\ \ ]-Wshadow[\ \ ]*) ;;
# *) CFLAGS="$CFLAGS -Wshadow" ;;
# esac
case " $CFLAGS " in
*[\ \ ]-Wchar-subscripts[\ \ ]*) ;;
*) CFLAGS="$CFLAGS -Wchar-subscripts" ;;
esac
case " $CFLAGS " in
*[\ \ ]-Wmissing-declarations[\ \ ]*) ;;
*) CFLAGS="$CFLAGS -Wmissing-declarations" ;;
esac
case " $CFLAGS " in
*[\ \ ]-Wmissing-prototypes[\ \ ]*) ;;
*) CFLAGS="$CFLAGS -Wmissing-prototypes" ;;
esac
case " $CFLAGS " in
*[\ \ ]-Wnested-externs[\ \ ]*) ;;
*) CFLAGS="$CFLAGS -Wnested-externs" ;;
esac
case " $CFLAGS " in
*[\ \ ]-Wpointer-arith[\ \ ]*) ;;
*) CFLAGS="$CFLAGS -Wpointer-arith" ;;
esac
case " $CFLAGS " in
*[\ \ ]-Wcast-align[\ \ ]*) ;;
*) CFLAGS="$CFLAGS -Wcast-align" ;;
esac
case " $CFLAGS " in
*[\ \ ]-Wsign-compare[\ \ ]*) ;;
*) CFLAGS="$CFLAGS -Wsign-compare" ;;
esac
if test "$enable_compile_warnings" = error; then
case " $CFLAGS " in
*[\ \ ]-Werror[\ \ ]*) ;;
*) CFLAGS="$CFLAGS -Werror" ;;
esac
fi
fi
fi
changequote([,])dnl
AC_CONFIG_FILES([
Makefile
doc/Makefile
@@ -574,7 +574,7 @@ if expr $MUTTER_MINOR_VERSION % 2 > /dev/null ; then
stable_version=`expr $MUTTER_MINOR_VERSION - 1`
echo "This is the UNSTABLE branch of mutter"
echo -n "Use 2.$stable_version.x for stable "
echo "(gnome-2-$stable_version branch in Subversion)"
echo "(gnome-2-$stable_version branch in git)"
else
echo "This is the stable branch of mutter"
fi

View File

@@ -22,6 +22,14 @@ This document has separate sections for each format version. You may
want to read the document in reverse order, since the base features
are discussed under version 1.
New Features in Theme Format Version 3.2
========================================
A new window type 'attached' is added for modal dialogs which are
attached to their parent window. (When the attach_modal_dialogs preference
is turned on.) If no style is defined for the 'attached' window type,
the 'border' window type will be used instead.
New Features in Theme Format Version 3.1
========================================

View File

@@ -82,6 +82,7 @@ te
th
tk
tr
ug
uk
vi
wa

608
po/ar.po

File diff suppressed because it is too large Load Diff

935
po/es.po

File diff suppressed because it is too large Load Diff

432
po/gl.po

File diff suppressed because it is too large Load Diff

460
po/he.po

File diff suppressed because it is too large Load Diff

420
po/nb.po

File diff suppressed because it is too large Load Diff

564
po/sl.po

File diff suppressed because it is too large Load Diff

1842
po/ug.po Normal file

File diff suppressed because it is too large Load Diff

1384
po/vi.po

File diff suppressed because it is too large Load Diff

View File

@@ -21,13 +21,19 @@ mutter_SOURCES= \
core/boxes.c \
core/boxes-private.h \
include/boxes.h \
compositor/cogl-utils.c \
compositor/cogl-utils.h \
compositor/compositor.c \
compositor/compositor-private.h \
compositor/meta-background-actor.c \
compositor/meta-background-actor.h \
compositor/meta-module.c \
compositor/meta-module.h \
compositor/meta-plugin.c \
compositor/meta-plugin-manager.c \
compositor/meta-plugin-manager.h \
compositor/meta-shadow-factory.c \
compositor/meta-shadow-factory-private.h \
compositor/meta-shaped-texture.c \
compositor/meta-shaped-texture.h \
compositor/meta-texture-tower.c \
@@ -36,12 +42,13 @@ mutter_SOURCES= \
compositor/meta-window-actor-private.h \
compositor/meta-window-group.c \
compositor/meta-window-group.h \
compositor/shadow.c \
compositor/shadow.h \
compositor/tidy/tidy-texture-frame.c \
compositor/tidy/tidy-texture-frame.h \
compositor/meta-window-shape.c \
compositor/meta-window-shape.h \
compositor/region-utils.c \
compositor/region-utils.h \
include/compositor.h \
include/meta-plugin.h \
include/meta-shadow-factory.h \
include/meta-window-actor.h \
include/compositor-mutter.h \
core/constraints.c \
@@ -167,6 +174,7 @@ libmutterinclude_base_headers = \
include/group.h \
include/keybindings.h \
include/meta-plugin.h \
include/meta-shadow-factory.h \
include/meta-window-actor.h
# Excluded from scanning for introspection but installed

104
src/compositor/cogl-utils.c Normal file
View File

@@ -0,0 +1,104 @@
/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
/*
* Utilities for use with Cogl
*
* Copyright 2010 Red Hat, Inc.
* Copyright 2010 Intel Corporation
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License as
* published by the Free Software Foundation; either version 2 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
* 02111-1307, USA.
*/
#include "cogl-utils.h"
/**
* meta_create_color_texture_4ub:
* @red:
* @green:
* @blue:
* @alpha:
*
* Creates a texture that is a single pixel with the specified
* unpremultiplied color components.
*
* Return value: (transfer full): a newly created Cogl texture
*/
CoglHandle
meta_create_color_texture_4ub (guint8 red,
guint8 green,
guint8 blue,
guint8 alpha)
{
CoglColor color;
guint8 pixel[4];
cogl_color_set_from_4ub (&color, red, green, blue, alpha);
cogl_color_premultiply (&color);
pixel[0] = cogl_color_get_red_byte (&color);
pixel[1] = cogl_color_get_green_byte (&color);
pixel[2] = cogl_color_get_blue_byte (&color);
pixel[3] = cogl_color_get_alpha_byte (&color);
return cogl_texture_new_from_data (1, 1,
COGL_TEXTURE_NONE,
COGL_PIXEL_FORMAT_RGBA_8888_PRE,
COGL_PIXEL_FORMAT_ANY,
4, pixel);
}
/* Based on gnome-shell/src/st/st-private.c:_st_create_texture_material.c */
/**
* meta_create_texture_material:
* @src_texture: (allow-none): texture to use initially for the layer
*
* Creates a material with a single layer. Using a common template
* allows sharing a shader for different uses in Mutter. To share the same
* shader with all other materials that are just texture plus opacity
* would require Cogl fixes.
* (See http://bugzilla.clutter-project.org/show_bug.cgi?id=2425)
*
* Return value: (transfer full): a newly created Cogl material
*/
CoglHandle
meta_create_texture_material (CoglHandle src_texture)
{
static CoglHandle texture_material_template = COGL_INVALID_HANDLE;
CoglHandle material;
/* We use a material that has a dummy texture as a base for all
texture materials. The idea is that only the Cogl texture object
would be different in the children so it is likely that Cogl will
be able to share GL programs between all the textures. */
if (G_UNLIKELY (texture_material_template == COGL_INVALID_HANDLE))
{
CoglHandle dummy_texture;
dummy_texture = meta_create_color_texture_4ub (0xff, 0xff, 0xff, 0xff);
texture_material_template = cogl_material_new ();
cogl_material_set_layer (texture_material_template, 0, dummy_texture);
cogl_handle_unref (dummy_texture);
}
material = cogl_material_copy (texture_material_template);
if (src_texture != COGL_INVALID_HANDLE)
cogl_material_set_layer (material, 0, src_texture);
return material;
}

View File

@@ -0,0 +1,34 @@
/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
/*
* Utilities for use with Cogl
*
* Copyright 2010 Red Hat, Inc.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License as
* published by the Free Software Foundation; either version 2 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
* 02111-1307, USA.
*/
#ifndef __META_COGL_UTILS_H__
#define __META_COGL_UTILS_H__
#include <cogl/cogl.h>
CoglHandle meta_create_color_texture_4ub (guint8 red,
guint8 green,
guint8 blue,
guint8 alpha);
CoglHandle meta_create_texture_material (CoglHandle src_texture);
#endif /* __META_COGL_UTILS_H__ */

View File

@@ -35,6 +35,7 @@ struct _MetaCompScreen
MetaScreen *screen;
ClutterActor *stage, *window_group, *overlay_group;
ClutterActor *background_actor;
ClutterActor *hidden_group;
GList *windows;
GHashTable *windows_by_xid;

View File

@@ -11,8 +11,10 @@
#include "compositor-mutter.h"
#include "xprops.h"
#include "prefs.h"
#include "meta-shadow-factory.h"
#include "meta-window-actor-private.h"
#include "meta-window-group.h"
#include "meta-background-actor.h"
#include "../core/window-private.h" /* to check window->hidden */
#include "../core/display-private.h" /* for meta_display_lookup_x_window() */
#include <X11/extensions/shape.h>
@@ -33,7 +35,7 @@ composite_at_least_version (MetaDisplay *display, int maj, int min)
return (major > maj || (major == maj && minor >= min));
}
static void sync_actor_stacking (GList *windows);
static void sync_actor_stacking (MetaCompScreen *info);
static void
meta_finish_workspace_switch (MetaCompScreen *info)
@@ -47,7 +49,7 @@ meta_finish_workspace_switch (MetaCompScreen *info)
/*
* Fix up stacking order in case the plugin messed it up.
*/
sync_actor_stacking (info->windows);
sync_actor_stacking (info);
/* printf ("... FINISHED DESKTOP SWITCH\n"); */
@@ -86,7 +88,7 @@ add_win (MetaWindow *window)
meta_window_actor_new (window);
sync_actor_stacking (info->windows);
sync_actor_stacking (info);
}
static void
@@ -135,6 +137,22 @@ process_property_notify (MetaCompositor *compositor,
{
MetaWindowActor *window_actor;
if (event->atom == compositor->atom_x_root_pixmap)
{
GSList *l;
for (l = meta_display_get_screens (compositor->display); l; l = l->next)
{
MetaScreen *screen = l->data;
if (event->window == meta_screen_get_xroot (screen))
{
MetaCompScreen *info = meta_screen_get_compositor_data (screen);
meta_background_actor_update (META_BACKGROUND_ACTOR (info->background_actor));
return;
}
}
}
if (window == NULL)
return;
@@ -235,6 +253,27 @@ meta_get_window_group_for_screen (MetaScreen *screen)
return info->window_group;
}
/**
* meta_get_background_actor_for_screen:
* @screen: a #MetaScreen
*
* Gets the actor that draws the root window background under the windows.
* The root window background automatically tracks the image or color set
* by the environment.
*
* Returns: (transfer none): The background actor corresponding to @screen
*/
ClutterActor *
meta_get_background_actor_for_screen (MetaScreen *screen)
{
MetaCompScreen *info = meta_screen_get_compositor_data (screen);
if (!info)
return NULL;
return info->background_actor;
}
/**
* meta_get_window_actors:
* @screen: a #MetaScreen
@@ -493,9 +532,14 @@ meta_compositor_manage_screen (MetaCompositor *compositor,
XSelectInput (xdisplay, xwin, event_mask);
info->window_group = meta_window_group_new (screen);
info->background_actor = meta_background_actor_new (screen);
info->overlay_group = clutter_group_new ();
info->hidden_group = clutter_group_new ();
clutter_container_add (CLUTTER_CONTAINER (info->window_group),
info->background_actor,
NULL);
clutter_container_add (CLUTTER_CONTAINER (info->stage),
info->window_group,
info->overlay_group,
@@ -826,18 +870,73 @@ meta_compositor_switch_workspace (MetaCompositor *compositor,
}
static void
sync_actor_stacking (GList *windows)
sync_actor_stacking (MetaCompScreen *info)
{
GList *children;
GList *tmp;
GList *old;
gboolean reordered;
/* NB: The first entry in the list is stacked the lowest */
/* NB: The first entries in the lists are stacked the lowest */
for (tmp = g_list_last (windows); tmp != NULL; tmp = tmp->prev)
/* Restacking will trigger full screen redraws, so it's worth a
* little effort to make sure we actually need to restack before
* we go ahead and do it */
children = clutter_container_get_children (CLUTTER_CONTAINER (info->window_group));
reordered = FALSE;
old = children;
/* We allow for actors in the window group other than the actors we
* know about, but it's up to a plugin to try and keep them stacked correctly
* (we really need extra API to make that reliable.)
*/
/* Of the actors we know, the bottom actor should be the background actor */
while (old && old->data != info->background_actor && !META_IS_WINDOW_ACTOR (old->data))
old = old->next;
if (old == NULL || old->data != info->background_actor)
{
reordered = TRUE;
goto done_with_check;
}
/* Then the window actors should follow in sequence */
old = old->next;
for (tmp = info->windows; tmp != NULL; tmp = tmp->next)
{
while (old && !META_IS_WINDOW_ACTOR (old->data))
old = old->next;
/* old == NULL: someone reparented a window out of the window group,
* order undefined, always restack */
if (old == NULL || old->data != tmp->data)
{
reordered = TRUE;
goto done_with_check;
}
old = old->next;
}
done_with_check:
g_list_free (children);
if (!reordered)
return;
for (tmp = g_list_last (info->windows); tmp != NULL; tmp = tmp->prev)
{
MetaWindowActor *window_actor = tmp->data;
clutter_actor_lower_bottom (CLUTTER_ACTOR (window_actor));
}
clutter_actor_lower_bottom (info->background_actor);
}
void
@@ -926,7 +1025,7 @@ meta_compositor_sync_stack (MetaCompositor *compositor,
old_stack = g_list_remove (old_stack, actor);
}
sync_actor_stacking (info->windows);
sync_actor_stacking (info);
}
void
@@ -983,6 +1082,8 @@ meta_compositor_sync_screen_size (MetaCompositor *compositor,
clutter_actor_set_size (info->stage, width, height);
meta_background_actor_screen_size_changed (META_BACKGROUND_ACTOR (info->background_actor));
meta_verbose ("Changed size for stage on screen %d to %dx%d\n",
meta_screen_get_screen_number (screen),
width, height);
@@ -1017,6 +1118,26 @@ meta_repaint_func (gpointer data)
return TRUE;
}
static void
on_shadow_factory_changed (MetaShadowFactory *factory,
MetaCompositor *compositor)
{
GSList *screens = meta_display_get_screens (compositor->display);
GList *l;
GSList *sl;
for (sl = screens; sl; sl = sl->next)
{
MetaScreen *screen = sl->data;
MetaCompScreen *info = meta_screen_get_compositor_data (screen);
if (!info)
continue;
for (l = info->windows; l; l = l->next)
meta_window_actor_invalidate_shadow (l->data);
}
}
/**
* meta_compositor_new: (skip)
*
@@ -1047,6 +1168,11 @@ meta_compositor_new (MetaDisplay *display)
XInternAtoms (xdisplay, atom_names, G_N_ELEMENTS (atom_names),
False, atoms);
g_signal_connect (meta_shadow_factory_get_default (),
"changed",
G_CALLBACK (on_shadow_factory_changed),
compositor);
compositor->atom_x_root_pixmap = atoms[0];
compositor->atom_x_set_root = atoms[1];
compositor->atom_net_wm_window_opacity = atoms[2];

View File

@@ -0,0 +1,414 @@
/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
/*
* meta-background-actor.c: Actor for painting the root window background
*
* Copyright 2009 Sander Dijkhuis
* Copyright 2010 Red Hat, Inc.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License as
* published by the Free Software Foundation; either version 2 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
* 02111-1307, USA.
*
* Portions adapted from gnome-shell/src/shell-global.c
*/
#include <config.h>
#define COGL_ENABLE_EXPERIMENTAL_API
#include <cogl/cogl-texture-pixmap-x11.h>
#include <X11/Xatom.h>
#include "cogl-utils.h"
#include "compositor-private.h"
#include "errors.h"
#include "meta-background-actor.h"
struct _MetaBackgroundActorClass
{
ClutterActorClass parent_class;
};
struct _MetaBackgroundActor
{
ClutterActor parent;
CoglHandle material;
MetaScreen *screen;
cairo_region_t *visible_region;
float texture_width;
float texture_height;
guint have_pixmap : 1;
};
G_DEFINE_TYPE (MetaBackgroundActor, meta_background_actor, CLUTTER_TYPE_ACTOR);
static void
update_wrap_mode (MetaBackgroundActor *self)
{
int width, height;
CoglMaterialWrapMode wrap_mode;
meta_screen_get_size (self->screen, &width, &height);
/* We turn off repeating when we have a full-screen pixmap to keep from
* getting artifacts from one side of the image sneaking into the other
* side of the image via bilinear filtering.
*/
if (width == self->texture_width && height == self->texture_height)
wrap_mode = COGL_MATERIAL_WRAP_MODE_CLAMP_TO_EDGE;
else
wrap_mode = COGL_MATERIAL_WRAP_MODE_REPEAT;
cogl_material_set_layer_wrap_mode (self->material, 0, wrap_mode);
}
static void
set_texture (MetaBackgroundActor *self,
CoglHandle texture)
{
MetaDisplay *display;
display = meta_screen_get_display (self->screen);
/* This may trigger destruction of an old texture pixmap, which, if
* the underlying X pixmap is already gone has the tendency to trigger
* X errors inside DRI. For safety, trap errors */
meta_error_trap_push (display);
cogl_material_set_layer (self->material, 0, texture);
meta_error_trap_pop (display);
self->texture_width = cogl_texture_get_width (texture);
self->texture_height = cogl_texture_get_height (texture);
update_wrap_mode (self);
clutter_actor_queue_redraw (CLUTTER_ACTOR (self));
}
/* Sets our material to paint with a 1x1 texture of the stage's background
* color; doing this when we have no pixmap allows the application to turn
* off painting the stage. There might be a performance benefit to
* painting in this case with a solid color, but the normal solid color
* case is a 1x1 root pixmap, so we'd have to reverse-engineer that to
* actually pick up the (small?) performance win. This is just a fallback.
*/
static void
set_texture_to_stage_color (MetaBackgroundActor *self)
{
ClutterActor *stage = meta_get_stage_for_screen (self->screen);
ClutterColor color;
CoglHandle texture;
clutter_stage_get_color (CLUTTER_STAGE (stage), &color);
texture = meta_create_color_texture_4ub (color.red, color.green,
color.blue, 0xff);
set_texture (self, texture);
cogl_handle_unref (texture);
}
static void
on_notify_stage_color (GObject *stage,
GParamSpec *pspec,
MetaBackgroundActor *self)
{
if (!self->have_pixmap)
set_texture_to_stage_color (self);
}
static void
meta_background_actor_dispose (GObject *object)
{
MetaBackgroundActor *self = META_BACKGROUND_ACTOR (object);
meta_background_actor_set_visible_region (self, NULL);
if (self->material != COGL_INVALID_HANDLE)
{
cogl_handle_unref (self->material);
self->material = COGL_INVALID_HANDLE;
}
if (self->screen != NULL)
{
ClutterActor *stage = meta_get_stage_for_screen (self->screen);
g_signal_handlers_disconnect_by_func (stage,
(gpointer) on_notify_stage_color,
self);
self->screen = NULL;
}
}
static void
meta_background_actor_get_preferred_width (ClutterActor *actor,
gfloat for_height,
gfloat *min_width_p,
gfloat *natural_width_p)
{
MetaBackgroundActor *self = META_BACKGROUND_ACTOR (actor);
int width, height;
meta_screen_get_size (self->screen, &width, &height);
if (min_width_p)
*min_width_p = width;
if (natural_width_p)
*natural_width_p = height;
}
static void
meta_background_actor_get_preferred_height (ClutterActor *actor,
gfloat for_width,
gfloat *min_height_p,
gfloat *natural_height_p)
{
MetaBackgroundActor *self = META_BACKGROUND_ACTOR (actor);
int width, height;
meta_screen_get_size (self->screen, &width, &height);
if (min_height_p)
*min_height_p = height;
if (natural_height_p)
*natural_height_p = height;
}
static void
meta_background_actor_paint (ClutterActor *actor)
{
MetaBackgroundActor *self = META_BACKGROUND_ACTOR (actor);
int width, height;
meta_screen_get_size (self->screen, &width, &height);
cogl_set_source (self->material);
if (self->visible_region)
{
int n_rectangles = cairo_region_num_rectangles (self->visible_region);
int i;
for (i = 0; i < n_rectangles; i++)
{
cairo_rectangle_int_t rect;
cairo_region_get_rectangle (self->visible_region, i, &rect);
cogl_rectangle_with_texture_coords (rect.x, rect.y,
rect.x + rect.width, rect.y + rect.height,
rect.x / self->texture_width,
rect.y / self->texture_height,
(rect.x + rect.width) / self->texture_width,
(rect.y + rect.height) / self->texture_height);
}
}
else
{
cogl_rectangle_with_texture_coords (0.0f, 0.0f,
width, height,
0.0f, 0.0f,
width / self->texture_width,
height / self->texture_height);
}
}
#if CLUTTER_CHECK_VERSION(1, 5, 2)
static gboolean
meta_background_actor_get_paint_volume (ClutterActor *actor,
ClutterPaintVolume *volume)
{
MetaBackgroundActor *self = META_BACKGROUND_ACTOR (actor);
int width, height;
meta_screen_get_size (self->screen, &width, &height);
clutter_paint_volume_set_width (volume, width);
clutter_paint_volume_set_height (volume, height);
return TRUE;
}
#endif
static void
meta_background_actor_class_init (MetaBackgroundActorClass *klass)
{
GObjectClass *object_class = G_OBJECT_CLASS (klass);
ClutterActorClass *actor_class = CLUTTER_ACTOR_CLASS (klass);
object_class->dispose = meta_background_actor_dispose;
actor_class->get_preferred_width = meta_background_actor_get_preferred_width;
actor_class->get_preferred_height = meta_background_actor_get_preferred_height;
actor_class->paint = meta_background_actor_paint;
#if CLUTTER_CHECK_VERSION(1, 5, 2)
actor_class->get_paint_volume = meta_background_actor_get_paint_volume;
#endif
}
static void
meta_background_actor_init (MetaBackgroundActor *background_actor)
{
}
/**
* @screen: the #MetaScreen
* meta_background_actor_new:
*
* Creates a new actor to draw the background for the given screen.
*
* Return value: (transfer none): the newly created background actor
*/
ClutterActor *
meta_background_actor_new (MetaScreen *screen)
{
MetaBackgroundActor *self;
ClutterActor *stage;
g_return_val_if_fail (META_IS_SCREEN (screen), NULL);
self = g_object_new (META_TYPE_BACKGROUND_ACTOR, NULL);
self->screen = screen;
self->material = meta_create_texture_material (NULL);
cogl_material_set_layer_wrap_mode (self->material, 0,
COGL_MATERIAL_WRAP_MODE_REPEAT);
stage = meta_get_stage_for_screen (self->screen);
g_signal_connect (stage, "notify::color",
G_CALLBACK (on_notify_stage_color), self);
meta_background_actor_update (self);
return CLUTTER_ACTOR (self);
}
/**
* meta_background_actor_update:
* @self: a #MetaBackgroundActor
*
* Refetches the _XROOTPMAP_ID property for the root window and updates
* the contents of the background actor based on that. There's no attempt
* to optimize out pixmap values that don't change (since a root pixmap
* could be replaced by with another pixmap with the same ID under some
* circumstances), so this should only be called when we actually receive
* a PropertyNotify event for the property.
*/
void
meta_background_actor_update (MetaBackgroundActor *self)
{
MetaDisplay *display;
MetaCompositor *compositor;
Atom type;
int format;
gulong nitems;
gulong bytes_after;
guchar *data;
Pixmap root_pixmap_id;
g_return_if_fail (META_IS_BACKGROUND_ACTOR (self));
display = meta_screen_get_display (self->screen);
compositor = meta_display_get_compositor (display);
root_pixmap_id = None;
if (!XGetWindowProperty (meta_display_get_xdisplay (display),
meta_screen_get_xroot (self->screen),
compositor->atom_x_root_pixmap,
0, LONG_MAX,
False,
AnyPropertyType,
&type, &format, &nitems, &bytes_after, &data) &&
type != None)
{
/* Got a property. */
if (type == XA_PIXMAP && format == 32 && nitems == 1)
{
/* Was what we expected. */
root_pixmap_id = *(Pixmap *)data;
}
XFree(data);
}
if (root_pixmap_id != None)
{
CoglHandle texture;
meta_error_trap_push (display);
texture = cogl_texture_pixmap_x11_new (root_pixmap_id, FALSE);
meta_error_trap_pop (display);
if (texture != COGL_INVALID_HANDLE)
{
set_texture (self, texture);
cogl_handle_unref (texture);
self->have_pixmap = True;
return;
}
}
self->have_pixmap = False;
set_texture_to_stage_color (self);
}
/**
* meta_background_actor_set_visible_region:
* @self: a #MetaBackgroundActor
* @visible_region: (allow-none): the area of the actor (in allocate-relative
* coordinates) that is visible.
*
* Sets the area of the background that is unobscured by overlapping windows.
* This is used to optimize and only paint the visible portions.
*/
void
meta_background_actor_set_visible_region (MetaBackgroundActor *self,
cairo_region_t *visible_region)
{
g_return_if_fail (META_IS_BACKGROUND_ACTOR (self));
if (self->visible_region)
{
cairo_region_destroy (self->visible_region);
self->visible_region = NULL;
}
if (visible_region)
{
cairo_rectangle_int_t screen_rect = { 0 };
meta_screen_get_size (self->screen, &screen_rect.width, &screen_rect.height);
/* Doing the intersection here is probably unnecessary - MetaWindowGroup
* should never compute a visible area that's larger than the root screen!
* but it's not that expensive and adds some extra robustness.
*/
self->visible_region = cairo_region_create_rectangle (&screen_rect);
cairo_region_intersect (self->visible_region, visible_region);
}
}
/**
* meta_background_actor_screen_size_changed:
* @self: a #MetaBackgroundActor
*
* Called by the compositor when the size of the #MetaScreen changes
*/
void
meta_background_actor_screen_size_changed (MetaBackgroundActor *self)
{
update_wrap_mode (self);
clutter_actor_queue_relayout (CLUTTER_ACTOR (self));
}

View File

@@ -0,0 +1,58 @@
/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
/*
* meta-background-actor.h: Actor for painting the root window background
*
* Copyright 2010 Red Hat, Inc.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License as
* published by the Free Software Foundation; either version 2 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
* 02111-1307, USA.
*/
#ifndef META_BACKGROUND_ACTOR_H
#define META_BACKGROUND_ACTOR_H
#include <clutter/clutter.h>
#include "screen.h"
/**
* MetaBackgroundActor:
*
* This class handles tracking and painting the root window background.
* By integrating with #MetaWindowGroup we can avoid painting parts of
* the background that are obscured by other windows.
*/
#define META_TYPE_BACKGROUND_ACTOR (meta_background_actor_get_type ())
#define META_BACKGROUND_ACTOR(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), META_TYPE_BACKGROUND_ACTOR, MetaBackgroundActor))
#define META_BACKGROUND_ACTOR_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), META_TYPE_BACKGROUND_ACTOR, MetaBackgroundActorClass))
#define META_IS_BACKGROUND_ACTOR(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), META_TYPE_BACKGROUND_ACTOR))
#define META_IS_BACKGROUND_ACTOR_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), META_TYPE_BACKGROUND_ACTOR))
#define META_BACKGROUND_ACTOR_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), META_TYPE_BACKGROUND_ACTOR, MetaBackgroundActorClass))
typedef struct _MetaBackgroundActor MetaBackgroundActor;
typedef struct _MetaBackgroundActorClass MetaBackgroundActorClass;
typedef struct _MetaBackgroundActorPrivate MetaBackgroundActorPrivate;
GType meta_background_actor_get_type (void);
ClutterActor *meta_background_actor_new (MetaScreen *screen);
void meta_background_actor_update (MetaBackgroundActor *actor);
void meta_background_actor_set_visible_region (MetaBackgroundActor *self,
cairo_region_t *visible_region);
void meta_background_actor_screen_size_changed (MetaBackgroundActor *self);
#endif /* META_BACKGROUND_ACTOR_H */

View File

@@ -351,6 +351,14 @@ meta_plugin_get_window_group (MetaPlugin *plugin)
return meta_get_window_group_for_screen (priv->screen);
}
ClutterActor *
meta_plugin_get_background_actor (MetaPlugin *plugin)
{
MetaPluginPrivate *priv = META_PLUGIN (plugin)->priv;
return meta_get_background_actor_for_screen (priv->screen);
}
/**
* _meta_plugin_effect_started:
* @plugin: the plugin

View File

@@ -0,0 +1,67 @@
/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
/*
* MetaShadowFactory:
*
* Create and cache shadow textures for arbitrary window shapes
*
* Copyright (C) 2010 Red Hat, Inc.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License as
* published by the Free Software Foundation; either version 2 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
* 02111-1307, USA.
*/
#ifndef __META_SHADOW_FACTORY_PRIVATE_H__
#define __META_SHADOW_FACTORY_PRIVATE_H__
#include <cairo.h>
#include <clutter/clutter.h>
#include "meta-window-shape.h"
#include "meta-shadow-factory.h"
/**
* MetaShadow:
* #MetaShadow holds a shadow texture along with information about how to
* apply that texture to draw a window texture. (E.g., it knows how big the
* unscaled borders are on each side of the shadow texture.)
*/
typedef struct _MetaShadow MetaShadow;
MetaShadow *meta_shadow_ref (MetaShadow *shadow);
void meta_shadow_unref (MetaShadow *shadow);
CoglHandle meta_shadow_get_texture (MetaShadow *shadow);
void meta_shadow_paint (MetaShadow *shadow,
int window_x,
int window_y,
int window_width,
int window_height,
guint8 opacity,
cairo_region_t *clip);
void meta_shadow_get_bounds (MetaShadow *shadow,
int window_x,
int window_y,
int window_width,
int window_height,
cairo_rectangle_int_t *bounds);
MetaShadowFactory *meta_shadow_factory_new (void);
MetaShadow *meta_shadow_factory_get_shadow (MetaShadowFactory *factory,
MetaWindowShape *shape,
int width,
int height,
const char *class_name,
gboolean focused);
#endif /* __META_SHADOW_FACTORY_PRIVATE_H__ */

View File

@@ -0,0 +1,989 @@
/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
/*
* MetaShadowFactory:
*
* Create and cache shadow textures for abritrary window shapes
*
* Copyright 2010 Red Hat, Inc.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License as
* published by the Free Software Foundation; either version 2 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
* 02111-1307, USA.
*/
#include <config.h>
#include <math.h>
#include <string.h>
#include "cogl-utils.h"
#include "meta-shadow-factory-private.h"
#include "region-utils.h"
/* This file implements blurring the shape of a window to produce a
* shadow texture. The details are discussed below; a quick summary
* of the optimizations we use:
*
* - If the window shape is along the lines of a rounded rectangle -
* a rectangular center portion with stuff at the corners - then
* the blur of this - the shadow - can also be represented as a
* 9-sliced texture and the same texture can be used for different
* size.
*
* - We use the fact that a Gaussian blur is separable to do a
* 2D blur as 1D blur of the rows followed by a 1D blur of the
* columns.
*
* - For better cache efficiency, we blur rows, transpose the image
* in blocks, blur rows again, and then transpose back.
*
* - We approximate the 1D gaussian blur as 3 successive box filters.
*/
typedef struct _MetaShadowCacheKey MetaShadowCacheKey;
typedef struct _MetaShadowClassInfo MetaShadowClassInfo;
struct _MetaShadowCacheKey
{
MetaWindowShape *shape;
int radius;
int top_fade;
};
struct _MetaShadow
{
int ref_count;
MetaShadowFactory *factory;
MetaShadowCacheKey key;
CoglHandle texture;
CoglHandle material;
/* The outer order is the distance the shadow extends outside the window
* shape; the inner border is the unscaled portion inside the window
* shape */
int outer_border_top;
int inner_border_top;
int outer_border_right;
int inner_border_right;
int outer_border_bottom;
int inner_border_bottom;
int outer_border_left;
int inner_border_left;
guint scale_width : 1;
guint scale_height : 1;
};
struct _MetaShadowClassInfo
{
const char *name; /* const so we can reuse for static definitions */
MetaShadowParams focused;
MetaShadowParams unfocused;
};
struct _MetaShadowFactory
{
GObject parent_instance;
/* MetaShadowCacheKey => MetaShadow; the shadows are not referenced
* by the factory, they are simply removed from the table when freed */
GHashTable *shadows;
/* class name => MetaShadowClassInfo */
GHashTable *shadow_classes;
};
struct _MetaShadowFactoryClass
{
GObjectClass parent_class;
};
enum
{
CHANGED,
LAST_SIGNAL
};
static guint signals[LAST_SIGNAL] = { 0 };
/* The first element in this array also defines the default parameters
* for newly created classes */
MetaShadowClassInfo default_shadow_classes[] = {
{ "normal", { 12, -1, 0, 8, 255 }, { 6, -1, 0, 4, 255 } },
{ "dialog", { 12, -1, 0, 8, 255 }, { 6, -1, 0, 4, 255 } },
{ "modal_dialog", { 12, -1, 0, 8, 255 }, { 6, -1, 0, 4, 255 } },
{ "utility", { 12, -1, 0, 8, 255 }, { 6, -1, 0, 4, 255 } },
{ "border", { 12, -1, 0, 8, 255 }, { 6, -1, 0, 4, 255 } },
{ "menu", { 12, -1, 0, 8, 255 }, { 6, -1, 0, 4, 255 } },
{ "popup-menu", { 6, -1, 0, 4, 255 }, { 6, -1, 0, 4, 255 } },
{ "dropdown-menu", { 6, 25, 0, 4, 255 }, { 6, 100, 0, 4, 255 } },
{ "attached", { 6, 25, 0, 4, 255 }, { 6, 100, 0, 4, 255 } }
};
G_DEFINE_TYPE (MetaShadowFactory, meta_shadow_factory, G_TYPE_OBJECT);
static guint
meta_shadow_cache_key_hash (gconstpointer val)
{
const MetaShadowCacheKey *key = val;
return 59 * key->radius + 67 * key->top_fade + 73 * meta_window_shape_hash (key->shape);
}
static gboolean
meta_shadow_cache_key_equal (gconstpointer a,
gconstpointer b)
{
const MetaShadowCacheKey *key_a = a;
const MetaShadowCacheKey *key_b = b;
return (key_a->radius == key_b->radius && key_a->top_fade == key_b->top_fade &&
meta_window_shape_equal (key_a->shape, key_b->shape));
}
MetaShadow *
meta_shadow_ref (MetaShadow *shadow)
{
shadow->ref_count++;
return shadow;
}
void
meta_shadow_unref (MetaShadow *shadow)
{
shadow->ref_count--;
if (shadow->ref_count == 0)
{
if (shadow->factory)
{
g_hash_table_remove (shadow->factory->shadows,
&shadow->key);
}
meta_window_shape_unref (shadow->key.shape);
cogl_handle_unref (shadow->texture);
cogl_handle_unref (shadow->material);
g_slice_free (MetaShadow, shadow);
}
}
/**
* meta_shadow_paint:
* @window_x: x position of the region to paint a shadow for
* @window_y: y position of the region to paint a shadow for
* @window_width: actual width of the region to paint a shadow for
* @window_height: actual height of the region to paint a shadow for
* @clip: (allow-none): if non-%NULL specifies the visible portion
* of the shadow. Drawing won't be strictly clipped to this region
* but it will be used to optimize what is drawn.
*
* Paints the shadow at the given position, for the specified actual
* size of the region. (Since a #MetaShadow can be shared between
* different sizes with the same extracted #MetaWindowShape the
* size needs to be passed in here.)
*/
void
meta_shadow_paint (MetaShadow *shadow,
int window_x,
int window_y,
int window_width,
int window_height,
guint8 opacity,
cairo_region_t *clip)
{
float texture_width = cogl_texture_get_width (shadow->texture);
float texture_height = cogl_texture_get_height (shadow->texture);
int i, j;
float src_x[4];
float src_y[4];
int dest_x[4];
int dest_y[4];
int n_x, n_y;
cogl_material_set_color4ub (shadow->material,
opacity, opacity, opacity, opacity);
cogl_set_source (shadow->material);
if (shadow->scale_width)
{
n_x = 3;
src_x[0] = 0.0;
src_x[1] = (shadow->inner_border_left + shadow->outer_border_left) / texture_width;
src_x[2] = (texture_width - (shadow->inner_border_right + shadow->outer_border_right)) / texture_width;
src_x[3] = 1.0;
dest_x[0] = window_x - shadow->outer_border_left;
dest_x[1] = window_x + shadow->inner_border_left;
dest_x[2] = window_x + window_width - shadow->inner_border_right;
dest_x[3] = window_x + window_width + shadow->outer_border_right;
}
else
{
n_x = 1;
src_x[0] = 0.0;
src_x[1] = 1.0;
dest_x[0] = window_x - shadow->outer_border_left;
dest_x[1] = window_x + window_width + shadow->outer_border_right;
}
if (shadow->scale_height)
{
n_y = 3;
src_y[0] = 0.0;
src_y[1] = (shadow->inner_border_top + shadow->outer_border_top) / texture_height;
src_y[2] = (texture_height - (shadow->inner_border_bottom + shadow->outer_border_bottom)) / texture_height;
src_y[3] = 1.0;
dest_y[0] = window_y - shadow->outer_border_top;
dest_y[1] = window_y + shadow->inner_border_top;
dest_y[2] = window_y + window_height - shadow->inner_border_bottom;
dest_y[3] = window_y + window_height + shadow->outer_border_bottom;
}
else
{
n_y = 1;
src_y[0] = 0.0;
src_y[1] = 1.0;
dest_y[0] = window_y - shadow->outer_border_top;
dest_y[1] = window_y + window_height + shadow->outer_border_bottom;
}
for (j = 0; j < n_y; j++)
{
cairo_rectangle_int_t dest_rect;
dest_rect.y = dest_y[j];
dest_rect.height = dest_y[j + 1] - dest_y[j];
for (i = 0; i < n_x; i++)
{
cairo_region_overlap_t overlap;
dest_rect.x = dest_x[i];
dest_rect.width = dest_x[i + 1] - dest_x[i];
if (clip)
overlap = cairo_region_contains_rectangle (clip, &dest_rect);
else
overlap = CAIRO_REGION_OVERLAP_PART;
if (overlap != CAIRO_REGION_OVERLAP_OUT)
cogl_rectangle_with_texture_coords (dest_x[i], dest_y[j],
dest_x[i + 1], dest_y[j + 1],
src_x[i], src_y[j],
src_x[i + 1], src_y[j + 1]);
}
}
}
/**
* meta_shadow_get_bounds:
* @shadow: a #MetaShadow
* @window_x: x position of the region to paint a shadow for
* @window_y: y position of the region to paint a shadow for
* @window_width: actual width of the region to paint a shadow for
* @window_height: actual height of the region to paint a shadow for
*
* Computes the bounds of the pixels that will be affected by
* meta_shadow_paints()
*/
void
meta_shadow_get_bounds (MetaShadow *shadow,
int window_x,
int window_y,
int window_width,
int window_height,
cairo_rectangle_int_t *bounds)
{
bounds->x = window_x - shadow->outer_border_left;
bounds->y = window_x - shadow->outer_border_top;
bounds->width = window_width + shadow->outer_border_left + shadow->outer_border_right;
bounds->height = window_height + shadow->outer_border_top + shadow->outer_border_bottom;
}
static void
meta_shadow_class_info_free (MetaShadowClassInfo *class_info)
{
g_free ((char *)class_info->name);
g_slice_free (MetaShadowClassInfo, class_info);
}
static void
meta_shadow_factory_init (MetaShadowFactory *factory)
{
guint i;
factory->shadows = g_hash_table_new (meta_shadow_cache_key_hash,
meta_shadow_cache_key_equal);
factory->shadow_classes = g_hash_table_new_full (g_str_hash,
g_str_equal,
NULL,
(GDestroyNotify)meta_shadow_class_info_free);
for (i = 0; i < G_N_ELEMENTS (default_shadow_classes); i++)
{
MetaShadowClassInfo *class_info = g_slice_new (MetaShadowClassInfo);
*class_info = default_shadow_classes[i];
class_info->name = g_strdup (class_info->name);
g_hash_table_insert (factory->shadow_classes,
(char *)class_info->name, class_info);
}
}
static void
meta_shadow_factory_finalize (GObject *object)
{
MetaShadowFactory *factory = META_SHADOW_FACTORY (object);
GHashTableIter iter;
gpointer key, value;
/* Detach from the shadows in the table so we won't try to
* remove them when they're freed. */
g_hash_table_iter_init (&iter, factory->shadows);
while (g_hash_table_iter_next (&iter, &key, &value))
{
MetaShadow *shadow = key;
shadow->factory = NULL;
}
g_hash_table_destroy (factory->shadows);
g_hash_table_destroy (factory->shadow_classes);
G_OBJECT_CLASS (meta_shadow_factory_parent_class)->finalize (object);
}
static void
meta_shadow_factory_class_init (MetaShadowFactoryClass *klass)
{
GObjectClass *object_class = G_OBJECT_CLASS (klass);
object_class->finalize = meta_shadow_factory_finalize;
signals[CHANGED] =
g_signal_new ("changed",
G_TYPE_FROM_CLASS (object_class),
G_SIGNAL_RUN_LAST,
0,
NULL, NULL,
g_cclosure_marshal_VOID__VOID,
G_TYPE_NONE, 0);
}
MetaShadowFactory *
meta_shadow_factory_new (void)
{
return g_object_new (META_TYPE_SHADOW_FACTORY, NULL);
}
/**
* meta_shadow_factory_get_default:
*
* Return value: (transfer none): the global singleton shadow factory
*/
MetaShadowFactory *
meta_shadow_factory_get_default (void)
{
static MetaShadowFactory *factory;
if (factory == NULL)
factory = meta_shadow_factory_new ();
return factory;
}
/* We emulate a 1D Gaussian blur by using 3 consecutive box blurs;
* this produces a result that's within 3% of the original and can be
* implemented much faster for large filter sizes because of the
* efficiency of implementation of a box blur. Idea and formula
* for choosing the box blur size come from:
*
* http://www.w3.org/TR/SVG/filters.html#feGaussianBlurElement
*
* The 2D blur is then done by blurring the rows, flipping the
* image and blurring the columns. (This is possible because the
* Gaussian kernel is separable - it's the product of a horizontal
* blur and a vertical blur.)
*/
static int
get_box_filter_size (int radius)
{
return (int)(0.5 + radius * (0.75 * sqrt(2*M_PI)));
}
/* The "spread" of the filter is the number of pixels from an original
* pixel that it's blurred image extends. (A no-op blur that doesn't
* blur would have a spread of 0.) See comment in blur_rows() for why the
* odd and even cases are different
*/
static int
get_shadow_spread (int radius)
{
int d = get_box_filter_size (radius);
if (d % 2 == 1)
return 3 * (d / 2);
else
return 3 * (d / 2) - 1;
}
/* This applies a single box blur pass to a horizontal range of pixels;
* since the box blur has the same weight for all pixels, we can
* implement an efficient sliding window algorithm where we add
* in pixels coming into the window from the right and remove
* them when they leave the windw to the left.
*
* d is the filter width; for even d shift indicates how the blurred
* result is aligned with the original - does ' x ' go to ' yy' (shift=1)
* or 'yy ' (shift=-1)
*/
static void
blur_xspan (guchar *row,
guchar *tmp_buffer,
int row_width,
int x0,
int x1,
int d,
int shift)
{
int offset;
int sum = 0;
int i;
if (d % 2 == 1)
offset = d / 2;
else
offset = (d - shift) / 2;
/* All the conditionals in here look slow, but the branches will
* be well predicted and there are enough different possibilities
* that trying to write this as a series of unconditional loops
* is hard and not an obvious win. The main slow down here seems
* to be the integer division for pixel; one possible optimization
* would be to accumulate into two 16-bit integer buffers and
* only divide down after all three passes. (SSE parallel implementation
* of the divide step is possible.)
*/
for (i = x0 - d + offset; i < x1 + offset; i++)
{
if (i >= 0 && i < row_width)
sum += row[i];
if (i >= x0 + offset)
{
if (i >= d)
sum -= row[i - d];
tmp_buffer[i - offset] = (sum + d / 2) / d;
}
}
memcpy(row + x0, tmp_buffer + x0, x1 - x0);
}
static void
blur_rows (cairo_region_t *convolve_region,
int x_offset,
int y_offset,
guchar *buffer,
int buffer_width,
int buffer_height,
int d)
{
int i, j;
int n_rectangles;
guchar *tmp_buffer;
tmp_buffer = g_malloc (buffer_width);
n_rectangles = cairo_region_num_rectangles (convolve_region);
for (i = 0; i < n_rectangles; i++)
{
cairo_rectangle_int_t rect;
cairo_region_get_rectangle (convolve_region, i, &rect);
for (j = y_offset + rect.y; j < y_offset + rect.y + rect.height; j++)
{
guchar *row = buffer + j * buffer_width;
int x0 = x_offset + rect.x;
int x1 = x0 + rect.width;
/* We want to produce a symmetric blur that spreads a pixel
* equally far to the left and right. If d is odd that happens
* naturally, but for d even, we approximate by using a blur
* on either side and then a centered blur of size d + 1.
* (techique also from the SVG specification)
*/
if (d % 2 == 1)
{
blur_xspan (row, tmp_buffer, buffer_width, x0, x1, d, 0);
blur_xspan (row, tmp_buffer, buffer_width, x0, x1, d, 0);
blur_xspan (row, tmp_buffer, buffer_width, x0, x1, d, 0);
}
else
{
blur_xspan (row, tmp_buffer, buffer_width, x0, x1, d, 1);
blur_xspan (row, tmp_buffer, buffer_width, x0, x1, d, -1);
blur_xspan (row, tmp_buffer, buffer_width, x0, x1, d + 1, 0);
}
}
}
g_free (tmp_buffer);
}
static void
fade_bytes (guchar *bytes,
int width,
int distance,
int total)
{
guint32 multiplier = (distance * 0x10000 + 0x8000) / total;
int i;
for (i = 0; i < width; i++)
bytes[i] = (bytes[i] * multiplier) >> 16;
}
/* Swaps width and height. Either swaps in-place and returns the original
* buffer or allocates a new buffer, frees the original buffer and returns
* the new buffer.
*/
static guchar *
flip_buffer (guchar *buffer,
int width,
int height)
{
/* Working in blocks increases cache efficiency, compared to reading
* or writing an entire column at once */
#define BLOCK_SIZE 16
if (width == height)
{
int i0, j0;
for (j0 = 0; j0 < height; j0 += BLOCK_SIZE)
for (i0 = 0; i0 <= j0; i0 += BLOCK_SIZE)
{
int max_j = MIN(j0 + BLOCK_SIZE, height);
int max_i = MIN(i0 + BLOCK_SIZE, width);
int i, j;
if (i0 == j0)
{
for (j = j0; j < max_j; j++)
for (i = i0; i < j; i++)
{
guchar tmp = buffer[j * width + i];
buffer[j * width + i] = buffer[i * width + j];
buffer[i * width + j] = tmp;
}
}
else
{
for (j = j0; j < max_j; j++)
for (i = i0; i < max_i; i++)
{
guchar tmp = buffer[j * width + i];
buffer[j * width + i] = buffer[i * width + j];
buffer[i * width + j] = tmp;
}
}
}
return buffer;
}
else
{
guchar *new_buffer = g_malloc (height * width);
int i0, j0;
for (i0 = 0; i0 < width; i0 += BLOCK_SIZE)
for (j0 = 0; j0 < height; j0 += BLOCK_SIZE)
{
int max_j = MIN(j0 + BLOCK_SIZE, height);
int max_i = MIN(i0 + BLOCK_SIZE, width);
int i, j;
for (i = i0; i < max_i; i++)
for (j = j0; j < max_j; j++)
new_buffer[i * height + j] = buffer[j * width + i];
}
g_free (buffer);
return new_buffer;
}
#undef BLOCK_SIZE
}
static void
make_shadow (MetaShadow *shadow,
cairo_region_t *region)
{
int d = get_box_filter_size (shadow->key.radius);
int spread = get_shadow_spread (shadow->key.radius);
cairo_rectangle_int_t extents;
cairo_region_t *row_convolve_region;
cairo_region_t *column_convolve_region;
guchar *buffer;
int buffer_width;
int buffer_height;
int x_offset;
int y_offset;
int n_rectangles, j, k;
cairo_region_get_extents (region, &extents);
/* In the case where top_fade >= 0 and the portion above the top
* edge of the shape will be cropped, it seems like we could create
* a smaller buffer and omit the top portion, but actually, in our
* multi-pass blur algorithm, the blur into the area above the window
* in the first pass will contribute back to the final pixel values
* for the top pixels, so we create a buffer as if we weren't cropping
* and only crop when creating the CoglTexture.
*/
buffer_width = extents.width + 2 * spread;
buffer_height = extents.height + 2 * spread;
/* Round up so we have aligned rows/columns */
buffer_width = (buffer_width + 3) & ~3;
buffer_height = (buffer_height + 3) & ~3;
/* Square buffer allows in-place swaps, which are roughly 70% faster, but we
* don't want to over-allocate too much memory.
*/
if (buffer_height < buffer_width && buffer_height > (3 * buffer_width) / 4)
buffer_height = buffer_width;
if (buffer_width < buffer_height && buffer_width > (3 * buffer_height) / 4)
buffer_width = buffer_height;
buffer = g_malloc0 (buffer_width * buffer_height);
/* Blurring with multiple box-blur passes is fast, but (especially for
* large shadow sizes) we can improve efficiency by restricting the blur
* to the region that actually needs to be blurred.
*/
row_convolve_region = meta_make_border_region (region, spread, spread, FALSE);
column_convolve_region = meta_make_border_region (region, 0, spread, TRUE);
/* Offsets between coordinates of the regions and coordinates in the buffer */
x_offset = spread;
y_offset = spread;
/* Step 1: unblurred image */
n_rectangles = cairo_region_num_rectangles (region);
for (k = 0; k < n_rectangles; k++)
{
cairo_rectangle_int_t rect;
cairo_region_get_rectangle (region, k, &rect);
for (j = y_offset + rect.y; j < y_offset + rect.y + rect.height; j++)
memset (buffer + buffer_width * j + x_offset + rect.x, 255, rect.width);
}
/* Step 2: swap rows and columns */
buffer = flip_buffer (buffer, buffer_width, buffer_height);
/* Step 3: blur rows (really columns) */
blur_rows (column_convolve_region, y_offset, x_offset,
buffer, buffer_height, buffer_width,
d);
/* Step 4: swap rows and columns */
buffer = flip_buffer (buffer, buffer_height, buffer_width);
/* Step 5: blur rows */
blur_rows (row_convolve_region, x_offset, y_offset,
buffer, buffer_width, buffer_height,
d);
/* Step 6: fade out the top, if applicable */
if (shadow->key.top_fade >= 0)
{
for (j = y_offset; j < y_offset + MIN (shadow->key.top_fade, extents.height + shadow->outer_border_bottom); j++)
fade_bytes(buffer + j * buffer_width, buffer_width, j - y_offset, shadow->key.top_fade);
}
/* We offset the passed in pixels to crop off the extra area we allocated at the top
* in the case of top_fade >= 0. We also account for padding at the left for symmetry
* though that doesn't currently occur.
*/
shadow->texture = cogl_texture_new_from_data (shadow->outer_border_left + extents.width + shadow->outer_border_right,
shadow->outer_border_top + extents.height + shadow->outer_border_bottom,
COGL_TEXTURE_NONE,
COGL_PIXEL_FORMAT_A_8,
COGL_PIXEL_FORMAT_ANY,
buffer_width,
(buffer +
(y_offset - shadow->outer_border_top) * buffer_width +
(x_offset - shadow->outer_border_left)));
cairo_region_destroy (row_convolve_region);
cairo_region_destroy (column_convolve_region);
g_free (buffer);
shadow->material = meta_create_texture_material (shadow->texture);
}
static MetaShadowParams *
get_shadow_params (MetaShadowFactory *factory,
const char *class_name,
gboolean focused,
gboolean create)
{
MetaShadowClassInfo *class_info = g_hash_table_lookup (factory->shadow_classes,
class_name);
if (class_info == NULL)
{
if (create)
{
class_info = g_slice_new0 (MetaShadowClassInfo);
*class_info = default_shadow_classes[0];
class_info->name = g_strdup (class_info->name);
g_hash_table_insert (factory->shadow_classes,
(char *)class_info->name, class_info);
}
else
{
class_info = &default_shadow_classes[0];
}
}
if (focused)
return &class_info->focused;
else
return &class_info->unfocused;
}
/**
* meta_shadow_factory_get_shadow:
* @factory: a #MetaShadowFactory
* @shape: the size-invariant shape of the window's region
* @width: the actual width of the window's region
* @height: the actual height of the window's region
* @class_name: name of the class of window shadows
* @focused: whether the shadow is for a focused window
*
* Gets the appropriate shadow object for drawing shadows for the
* specified window shape. The region that we are shadowing is specified
* as a combination of a size-invariant extracted shape and the size.
* In some cases, the same shadow object can be shared between sizes;
* in other cases a different shadow object is used for each size.
*
* Return value: (transfer full): a newly referenced #MetaShadow; unref with
* meta_shadow_unref()
*/
MetaShadow *
meta_shadow_factory_get_shadow (MetaShadowFactory *factory,
MetaWindowShape *shape,
int width,
int height,
const char *class_name,
gboolean focused)
{
MetaShadowParams *params;
MetaShadowCacheKey key;
MetaShadow *shadow;
cairo_region_t *region;
int spread;
int shape_border_top, shape_border_right, shape_border_bottom, shape_border_left;
int inner_border_top, inner_border_right, inner_border_bottom, inner_border_left;
int outer_border_top, outer_border_right, outer_border_bottom, outer_border_left;
gboolean scale_width, scale_height;
gboolean cacheable;
int center_width, center_height;
g_return_val_if_fail (META_IS_SHADOW_FACTORY (factory), NULL);
g_return_val_if_fail (shape != NULL, NULL);
/* Using a single shadow texture for different window sizes only works
* when there is a central scaled area that is greater than twice
* the spread of the gaussian blur we are applying to get to the
* shadow image.
* ********* ***********
* /----------\ *###########* *#############*
* | | => **#*********#** => **#***********#**
* | | **#** **#** **#** **#**
* | | **#*********#** **#***********#**
* \----------/ *###########* *#############*
* ********** ************
* Original Blur Stretched Blur
*
* For smaller sizes, we create a separate shadow image for each size;
* since we assume that there will be little reuse, we don't try to
* cache such images but just recreate them. (Since the current cache
* policy is to only keep around referenced shadows, there wouldn't
* be any harm in caching them, it would just make the book-keeping
* a bit tricker.)
*
* In the case where we are fading a the top, that also has to fit
* within the top unscaled border.
*/
params = get_shadow_params (factory, class_name, focused, FALSE);
spread = get_shadow_spread (params->radius);
meta_window_shape_get_borders (shape,
&shape_border_top,
&shape_border_right,
&shape_border_bottom,
&shape_border_left);
inner_border_top = MAX (shape_border_top + spread, params->top_fade);
outer_border_top = params->top_fade >= 0 ? 0 : spread;
inner_border_right = shape_border_right + spread;
outer_border_right = spread;
inner_border_bottom = shape_border_bottom + spread;
outer_border_bottom = spread;
inner_border_left = shape_border_left + spread;
outer_border_left = spread;
scale_width = inner_border_left + inner_border_right <= width;
scale_height = inner_border_top + inner_border_bottom <= height;
cacheable = scale_width && scale_height;
if (cacheable)
{
key.shape = shape;
key.radius = params->radius;
key.top_fade = params->top_fade;
shadow = g_hash_table_lookup (factory->shadows, &key);
if (shadow)
return meta_shadow_ref (shadow);
}
shadow = g_slice_new0 (MetaShadow);
shadow->ref_count = 1;
shadow->factory = factory;
shadow->key.shape = meta_window_shape_ref (shape);
shadow->key.radius = params->radius;
shadow->key.top_fade = params->top_fade;
shadow->outer_border_top = outer_border_top;
shadow->inner_border_top = inner_border_top;
shadow->outer_border_right = outer_border_right;
shadow->inner_border_right = inner_border_right;
shadow->outer_border_bottom = outer_border_bottom;
shadow->inner_border_bottom = inner_border_bottom;
shadow->outer_border_left = outer_border_left;
shadow->inner_border_left = inner_border_left;
shadow->scale_width = scale_width;
if (scale_width)
center_width = inner_border_left + inner_border_right - (shape_border_left + shape_border_right);
else
center_width = width - (shape_border_left + shape_border_right);
shadow->scale_height = scale_height;
if (scale_height)
center_height = inner_border_top + inner_border_bottom - (shape_border_top + shape_border_bottom);
else
center_height = height - (shape_border_top + shape_border_bottom);
g_assert (center_width >= 0 && center_height >= 0);
region = meta_window_shape_to_region (shape, center_width, center_height);
make_shadow (shadow, region);
cairo_region_destroy (region);
if (cacheable)
g_hash_table_insert (factory->shadows, &shadow->key, shadow);
return shadow;
}
/**
* meta_shadow_factory_set_params:
* @factory: a #MetaShadowFactory
* @class_name: name of the class of shadow to set the params for.
* the default shadow classes are the names of the different
* theme frame types (normal, dialog, modal_dialog, utility,
* border, menu, attached) and in addition, popup-menu
* and dropdown-menu.
* @focused: whether the shadow is for a focused window
* @params: new parameter values
*
* Updates the shadow parameters for a particular class of shadows
* for either the focused or unfocused state. If the class name
* does not name an existing class, a new class will be created
* (the other focus state for that class will have default values
* assigned to it.)
*/
void
meta_shadow_factory_set_params (MetaShadowFactory *factory,
const char *class_name,
gboolean focused,
MetaShadowParams *params)
{
MetaShadowParams *stored_params;
g_return_if_fail (META_IS_SHADOW_FACTORY (factory));
g_return_if_fail (class_name != NULL);
g_return_if_fail (params != NULL);
g_return_if_fail (params->radius >= 0);
stored_params = get_shadow_params (factory, class_name, focused, TRUE);
*stored_params = *params;
g_signal_emit (factory, signals[CHANGED], 0);
}
/**
* meta_shadow_factory_get_params:
* @factory: a #MetaShadowFactory
* @class_name: name of the class of shadow to get the params for
* @focused: whether the shadow is for a focused window
* @params: (out caller-allocates): location to store the current parameter values
*
* Gets the shadow parameters for a particular class of shadows
* for either the focused or unfocused state. If the class name
* does not name an existing class, default values will be returned
* without printing an error.
*/
void
meta_shadow_factory_get_params (MetaShadowFactory *factory,
const char *class_name,
gboolean focused,
MetaShadowParams *params)
{
MetaShadowParams *stored_params;
g_return_if_fail (META_IS_SHADOW_FACTORY (factory));
g_return_if_fail (class_name != NULL);
stored_params = get_shadow_params (factory, class_name, focused, FALSE);
if (params)
*params = *stored_params;
}

View File

@@ -28,6 +28,8 @@ void meta_window_actor_process_damage (MetaWindowActor *self,
XDamageNotifyEvent *event);
void meta_window_actor_pre_paint (MetaWindowActor *self);
void meta_window_actor_invalidate_shadow (MetaWindowActor *self);
gboolean meta_window_actor_effect_in_progress (MetaWindowActor *self);
void meta_window_actor_sync_actor_position (MetaWindowActor *self);
void meta_window_actor_sync_visibility (MetaWindowActor *self);

View File

@@ -9,6 +9,7 @@
#include <X11/extensions/Xrender.h>
#include <clutter/x11/clutter-x11.h>
#include <gdk/gdk.h> /* for gdk_rectangle_union() */
#include "display.h"
#include "errors.h"
@@ -17,10 +18,9 @@
#include "xprops.h"
#include "compositor-private.h"
#include "meta-shadow-factory-private.h"
#include "meta-shaped-texture.h"
#include "meta-window-actor-private.h"
#include "shadow.h"
#include "tidy/tidy-texture-frame.h"
struct _MetaWindowActorPrivate
{
@@ -31,12 +31,27 @@ struct _MetaWindowActorPrivate
MetaScreen *screen;
ClutterActor *actor;
ClutterActor *shadow;
/* MetaShadowFactory only caches shadows that are actually in use;
* to avoid unnecessary recomputation we do two things: 1) we store
* both a focused and unfocused shadow for the window. If the window
* doesn't have different focused and unfocused shadow parameters,
* these will be the same. 2) when the shadow potentially changes we
* don't immediately unreference the old shadow, we just flag it as
* dirty and recompute it when we next need it (recompute_focused_shadow,
* recompute_unfocused_shadow.) Because of our extraction of
* size-invariant window shape, we'll often find that the new shadow
* is the same as the old shadow.
*/
MetaShadow *focused_shadow;
MetaShadow *unfocused_shadow;
Pixmap back_pixmap;
Damage damage;
guint8 opacity;
guint8 shadow_opacity;
gchar * desc;
@@ -45,9 +60,16 @@ struct _MetaWindowActorPrivate
/* A rectangular region with the unshaped extends of the window
* texture */
cairo_region_t *bounding_region;
/* The region we should clip to when painting the shadow */
cairo_region_t *shadow_clip;
/* Extracted size-invariant shape used for shadows */
MetaWindowShape *shadow_shape;
gint freeze_count;
char * shadow_class;
/*
* These need to be counters rather than flags, since more plugins
* can implement same effect; the practicality of stacking effects
@@ -70,7 +92,9 @@ struct _MetaWindowActorPrivate
guint received_damage : 1;
guint needs_pixmap : 1;
guint needs_reshape : 1;
guint needs_reshape : 1;
guint recompute_focused_shadow : 1;
guint recompute_unfocused_shadow : 1;
guint size_changed : 1;
guint needs_destroy : 1;
@@ -87,8 +111,13 @@ enum
PROP_X_WINDOW,
PROP_X_WINDOW_ATTRIBUTES,
PROP_NO_SHADOW,
PROP_SHADOW_CLASS
};
#define DEFAULT_SHADOW_RADIUS 12
#define DEFAULT_SHADOW_X_OFFSET 0
#define DEFAULT_SHADOW_Y_OFFSET 8
static void meta_window_actor_dispose (GObject *object);
static void meta_window_actor_finalize (GObject *object);
static void meta_window_actor_constructed (GObject *object);
@@ -101,11 +130,18 @@ static void meta_window_actor_get_property (GObject *object,
GValue *value,
GParamSpec *pspec);
static void meta_window_actor_paint (ClutterActor *actor);
#if CLUTTER_CHECK_VERSION(1, 5, 2)
static gboolean meta_window_actor_get_paint_volume (ClutterActor *actor,
ClutterPaintVolume *volume);
#endif
static void meta_window_actor_detach (MetaWindowActor *self);
static gboolean meta_window_actor_has_shadow (MetaWindowActor *self);
static void meta_window_actor_clear_shape_region (MetaWindowActor *self);
static void meta_window_actor_clear_bounding_region (MetaWindowActor *self);
static void meta_window_actor_clear_shadow_clip (MetaWindowActor *self);
static gboolean is_shaped (MetaDisplay *display,
Window xwindow);
@@ -161,6 +197,7 @@ static void
meta_window_actor_class_init (MetaWindowActorClass *klass)
{
GObjectClass *object_class = G_OBJECT_CLASS (klass);
ClutterActorClass *actor_class = CLUTTER_ACTOR_CLASS (klass);
GParamSpec *pspec;
g_type_class_add_private (klass, sizeof (MetaWindowActorPrivate));
@@ -171,6 +208,11 @@ meta_window_actor_class_init (MetaWindowActorClass *klass)
object_class->get_property = meta_window_actor_get_property;
object_class->constructed = meta_window_actor_constructed;
actor_class->paint = meta_window_actor_paint;
#if CLUTTER_CHECK_VERSION(1, 5, 2)
actor_class->get_paint_volume = meta_window_actor_get_paint_volume;
#endif
pspec = g_param_spec_object ("meta-window",
"MetaWindow",
"The displayed MetaWindow",
@@ -216,11 +258,21 @@ meta_window_actor_class_init (MetaWindowActorClass *klass)
"No shadow",
"Do not add shaddow to this window",
FALSE,
G_PARAM_READWRITE | G_PARAM_CONSTRUCT);
G_PARAM_READWRITE);
g_object_class_install_property (object_class,
PROP_NO_SHADOW,
pspec);
pspec = g_param_spec_string ("shadow-class",
"Name of the shadow class for this window.",
"NULL means to use the default shadow class for this window type",
NULL,
G_PARAM_READWRITE);
g_object_class_install_property (object_class,
PROP_SHADOW_CLASS,
pspec);
}
static void
@@ -232,6 +284,7 @@ meta_window_actor_init (MetaWindowActor *self)
META_TYPE_WINDOW_ACTOR,
MetaWindowActorPrivate);
priv->opacity = 0xff;
priv->shadow_class = NULL;
}
static void
@@ -291,18 +344,6 @@ window_decorated_notify (MetaWindow *mw,
g_object_set (self, "x-window-attributes", &attrs, NULL);
if (priv->shadow)
{
ClutterActor *p = clutter_actor_get_parent (priv->shadow);
if (CLUTTER_IS_CONTAINER (p))
clutter_container_remove_actor (CLUTTER_CONTAINER (p), priv->shadow);
else
clutter_actor_unparent (priv->shadow);
priv->shadow = NULL;
}
/*
* Recreate the contents.
*/
@@ -344,13 +385,6 @@ meta_window_actor_constructed (GObject *object)
meta_window_actor_update_opacity (self);
if (meta_window_actor_has_shadow (self))
{
priv->shadow = meta_create_shadow_frame (compositor);
clutter_container_add_actor (CLUTTER_CONTAINER (self), priv->shadow);
}
if (!priv->actor)
{
priv->actor = meta_shaped_texture_new ();
@@ -406,6 +440,31 @@ meta_window_actor_dispose (GObject *object)
meta_window_actor_clear_shape_region (self);
meta_window_actor_clear_bounding_region (self);
meta_window_actor_clear_shadow_clip (self);
if (priv->shadow_class != NULL)
{
g_free (priv->shadow_class);
priv->shadow_class = NULL;
}
if (priv->focused_shadow != NULL)
{
meta_shadow_unref (priv->focused_shadow);
priv->focused_shadow = NULL;
}
if (priv->unfocused_shadow != NULL)
{
meta_shadow_unref (priv->unfocused_shadow);
priv->unfocused_shadow = NULL;
}
if (priv->shadow_shape != NULL)
{
meta_window_shape_unref (priv->shadow_shape);
priv->shadow_shape = NULL;
}
if (priv->damage != None)
{
@@ -463,36 +522,27 @@ meta_window_actor_set_property (GObject *object,
break;
case PROP_NO_SHADOW:
{
gboolean oldv = priv->no_shadow ? TRUE : FALSE;
gboolean newv = g_value_get_boolean (value);
if (oldv == newv)
if (newv == priv->no_shadow)
return;
priv->no_shadow = newv;
if (newv && priv->shadow)
{
clutter_container_remove_actor (CLUTTER_CONTAINER (object),
priv->shadow);
priv->shadow = NULL;
}
else if (!newv && !priv->shadow && meta_window_actor_has_shadow (self))
{
gfloat w, h;
MetaDisplay *display = meta_screen_get_display (priv->screen);
MetaCompositor *compositor;
meta_window_actor_invalidate_shadow (self);
}
break;
case PROP_SHADOW_CLASS:
{
const char *newv = g_value_get_string (value);
compositor = meta_display_get_compositor (display);
if (g_strcmp0 (newv, priv->shadow_class) == 0)
return;
clutter_actor_get_size (CLUTTER_ACTOR (self), &w, &h);
g_free (priv->shadow_class);
priv->shadow_class = g_strdup (newv);
priv->shadow = meta_create_shadow_frame (compositor);
clutter_actor_set_size (priv->shadow, w, h);
clutter_container_add_actor (CLUTTER_CONTAINER (self), priv->shadow);
}
meta_window_actor_invalidate_shadow (self);
}
break;
default:
@@ -526,12 +576,166 @@ meta_window_actor_get_property (GObject *object,
case PROP_NO_SHADOW:
g_value_set_boolean (value, priv->no_shadow);
break;
case PROP_SHADOW_CLASS:
g_value_set_string (value, priv->shadow_class);
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break;
}
}
static const char *
meta_window_actor_get_shadow_class (MetaWindowActor *self)
{
MetaWindowActorPrivate *priv = self->priv;
if (priv->shadow_class != NULL)
return priv->shadow_class;
else
{
MetaWindowType window_type = meta_window_get_window_type (priv->window);
switch (window_type)
{
case META_WINDOW_DROPDOWN_MENU:
return "dropdown-menu";
case META_WINDOW_POPUP_MENU:
return "popup-menu";
default:
{
MetaFrameType frame_type = meta_window_get_frame_type (priv->window);
return meta_frame_type_to_string (frame_type);
}
}
}
}
static void
meta_window_actor_get_shadow_params (MetaWindowActor *self,
gboolean appears_focused,
MetaShadowParams *params)
{
const char *shadow_class = meta_window_actor_get_shadow_class (self);
meta_shadow_factory_get_params (meta_shadow_factory_get_default (),
shadow_class, appears_focused,
params);
}
static void
meta_window_actor_get_shape_bounds (MetaWindowActor *self,
cairo_rectangle_int_t *bounds)
{
MetaWindowActorPrivate *priv = self->priv;
/* We need to be defensive here because there are corner cases
* where getting the shape fails on a window being destroyed
* and similar.
*/
if (priv->shaped && priv->shape_region)
cairo_region_get_extents (priv->shape_region, bounds);
else if (priv->bounding_region)
cairo_region_get_extents (priv->bounding_region, bounds);
else
bounds->x = bounds->y = bounds->width = bounds->height = 0;
}
#if CLUTTER_CHECK_VERSION(1, 5, 2)
static void
meta_window_actor_get_shadow_bounds (MetaWindowActor *self,
gboolean appears_focused,
cairo_rectangle_int_t *bounds)
{
MetaWindowActorPrivate *priv = self->priv;
MetaShadow *shadow = appears_focused ? priv->focused_shadow : priv->unfocused_shadow;
cairo_rectangle_int_t shape_bounds;
MetaShadowParams params;
meta_window_actor_get_shape_bounds (self, &shape_bounds);
meta_window_actor_get_shadow_params (self, appears_focused, &params);
meta_shadow_get_bounds (shadow,
params.x_offset + shape_bounds.x,
params.y_offset + shape_bounds.y,
shape_bounds.width,
shape_bounds.height,
bounds);
}
#endif
static void
meta_window_actor_paint (ClutterActor *actor)
{
MetaWindowActor *self = META_WINDOW_ACTOR (actor);
MetaWindowActorPrivate *priv = self->priv;
gboolean appears_focused = meta_window_appears_focused (priv->window);
MetaShadow *shadow = appears_focused ? priv->focused_shadow : priv->unfocused_shadow;
if (shadow != NULL)
{
MetaShadowParams params;
cairo_rectangle_int_t shape_bounds;
meta_window_actor_get_shape_bounds (self, &shape_bounds);
meta_window_actor_get_shadow_params (self, appears_focused, &params);
meta_shadow_paint (shadow,
params.x_offset + shape_bounds.x,
params.y_offset + shape_bounds.y,
shape_bounds.width,
shape_bounds.height,
(clutter_actor_get_paint_opacity (actor) * params.opacity) / 255,
priv->shadow_clip);
}
CLUTTER_ACTOR_CLASS (meta_window_actor_parent_class)->paint (actor);
}
#if CLUTTER_CHECK_VERSION(1, 5, 2)
static gboolean
meta_window_actor_get_paint_volume (ClutterActor *actor,
ClutterPaintVolume *volume)
{
MetaWindowActor *self = META_WINDOW_ACTOR (actor);
MetaWindowActorPrivate *priv = self->priv;
cairo_rectangle_int_t bounds;
gboolean appears_focused = meta_window_appears_focused (priv->window);
ClutterVertex origin;
/* The paint volume is computed before paint functions are called
* so our bounds might not be updated yet. Force an update. */
meta_window_actor_pre_paint (self);
meta_window_actor_get_shape_bounds (self, &bounds);
if (appears_focused ? priv->focused_shadow : priv->unfocused_shadow)
{
cairo_rectangle_int_t shadow_bounds;
/* We could compute an full clip region as we do for the window
* texture, but the shadow is relatively cheap to draw, and
* a little more complex to clip, so we just catch the case where
* the shadow is completely obscured and doesn't need to be drawn
* at all.
*/
meta_window_actor_get_shadow_bounds (self, appears_focused, &shadow_bounds);
gdk_rectangle_union (&bounds, &shadow_bounds, &bounds);
}
origin.x = bounds.x;
origin.y = bounds.y;
origin.z = 0.0f;
clutter_paint_volume_set_origin (volume, &origin);
clutter_paint_volume_set_width (volume, bounds.width);
clutter_paint_volume_set_height (volume, bounds.height);
return TRUE;
}
#endif /* CLUTTER_CHECK_VERSION */
static gboolean
is_shaped (MetaDisplay *display, Window xwindow)
{
@@ -560,10 +764,16 @@ meta_window_actor_has_shadow (MetaWindowActor *self)
if (priv->no_shadow)
return FALSE;
/* Leaving out shadows for maximized and fullscreen windows is an effeciency
* win and also prevents the unsightly effect of the shadow of maximized
* window appearing on an adjacent window */
if ((meta_window_get_maximized (priv->window) == (META_MAXIMIZE_HORIZONTAL | META_MAXIMIZE_VERTICAL)) ||
meta_window_is_fullscreen (priv->window))
return FALSE;
/*
* Always put a shadow around windows with a frame - This should override
* the restriction about not putting a shadow around shaped windows
* as the frame might be the reason the window is shaped
* the restriction about not putting a shadow around ARGB windows.
*/
if (priv->window)
{
@@ -576,7 +786,8 @@ meta_window_actor_has_shadow (MetaWindowActor *self)
}
/*
* Do not add shadows to ARGB windows (since they are probably transparent)
* Do not add shadows to ARGB windows; eventually we should generate a
* shadow from the input shape for such windows.
*/
if (priv->argb32 || priv->opacity != 0xff)
{
@@ -585,19 +796,8 @@ meta_window_actor_has_shadow (MetaWindowActor *self)
return FALSE;
}
/*
* Never put a shadow around shaped windows
*/
if (priv->shaped)
{
meta_verbose ("Window 0x%x has no shadow as it is shaped\n",
(guint)priv->xwindow);
return FALSE;
}
/*
* Add shadows to override redirect windows (e.g., Gtk menus).
* This must have lower priority than window shape test.
*/
if (priv->attrs.override_redirect)
{
@@ -1358,6 +1558,18 @@ meta_window_actor_clear_bounding_region (MetaWindowActor *self)
}
}
static void
meta_window_actor_clear_shadow_clip (MetaWindowActor *self)
{
MetaWindowActorPrivate *priv = self->priv;
if (priv->shadow_clip)
{
cairo_region_destroy (priv->shadow_clip);
priv->shadow_clip = NULL;
}
}
static void
meta_window_actor_update_bounding_region (MetaWindowActor *self,
int width,
@@ -1366,9 +1578,26 @@ meta_window_actor_update_bounding_region (MetaWindowActor *self,
MetaWindowActorPrivate *priv = self->priv;
cairo_rectangle_int_t bounding_rectangle = { 0, 0, width, height };
if (priv->bounding_region != NULL)
{
cairo_rectangle_int_t old_bounding_rectangle;
cairo_region_get_extents (priv->bounding_region, &old_bounding_rectangle);
if (old_bounding_rectangle.width == width && old_bounding_rectangle.height == height)
return;
}
meta_window_actor_clear_bounding_region (self);
priv->bounding_region = cairo_region_create_rectangle (&bounding_rectangle);
/* When we're shaped, we use the shape region to generate the shadow; the shape
* region only changes when we get ShapeNotify event; but for unshaped windows
* we generate the shadow from the bounding region, so we need to recompute
* the shadow when the size changes.
*/
if (!priv->shaped)
meta_window_actor_invalidate_shadow (self);
}
static void
@@ -1497,30 +1726,12 @@ meta_window_actor_set_visible_region_beneath (MetaWindowActor *self,
cairo_region_t *beneath_region)
{
MetaWindowActorPrivate *priv = self->priv;
gboolean appears_focused = meta_window_appears_focused (priv->window);
if (priv->shadow)
if (appears_focused ? priv->focused_shadow : priv->unfocused_shadow)
{
cairo_rectangle_int_t shadow_rect;
ClutterActorBox box;
cairo_region_overlap_t overlap;
/* We could compute an full clip region as we do for the window
* texture, but the shadow is relatively cheap to draw, and
* a little more complex to clip, so we just catch the case where
* the shadow is completely obscured and doesn't need to be drawn
* at all.
*/
clutter_actor_get_allocation_box (priv->shadow, &box);
shadow_rect.x = roundf (box.x1);
shadow_rect.y = roundf (box.y1);
shadow_rect.width = roundf (box.x2 - box.x1);
shadow_rect.height = roundf (box.y2 - box.y1);
overlap = cairo_region_contains_rectangle (beneath_region, &shadow_rect);
tidy_texture_frame_set_needs_paint (TIDY_TEXTURE_FRAME (priv->shadow),
overlap != CAIRO_REGION_OVERLAP_OUT);
meta_window_actor_clear_shadow_clip (self);
priv->shadow_clip = cairo_region_copy (beneath_region);
}
}
@@ -1538,8 +1749,7 @@ meta_window_actor_reset_visible_regions (MetaWindowActor *self)
meta_shaped_texture_set_clip_region (META_SHAPED_TEXTURE (priv->actor),
NULL);
if (priv->shadow)
tidy_texture_frame_set_needs_paint (TIDY_TEXTURE_FRAME (priv->shadow), TRUE);
meta_window_actor_clear_shadow_clip (self);
}
static void
@@ -1613,8 +1823,8 @@ check_needs_pixmap (MetaWindowActor *self)
* do it here.
* See: http://bugzilla.clutter-project.org/show_bug.cgi?id=2236
*/
if (!clutter_glx_texture_pixmap_using_extension (
CLUTTER_GLX_TEXTURE_PIXMAP (priv->actor)))
if (G_UNLIKELY (!clutter_glx_texture_pixmap_using_extension (
CLUTTER_GLX_TEXTURE_PIXMAP (priv->actor))))
g_warning ("NOTE: Not using GLX TFP!\n");
g_object_get (priv->actor,
@@ -1622,9 +1832,6 @@ check_needs_pixmap (MetaWindowActor *self)
"pixmap-height", &pxm_height,
NULL);
if (priv->shadow)
clutter_actor_set_size (priv->shadow, pxm_width, pxm_height);
meta_window_actor_update_bounding_region (self, pxm_width, pxm_height);
full = TRUE;
@@ -1635,6 +1842,79 @@ check_needs_pixmap (MetaWindowActor *self)
priv->needs_pixmap = FALSE;
}
static void
check_needs_shadow (MetaWindowActor *self)
{
MetaWindowActorPrivate *priv = self->priv;
MetaShadow *old_shadow = NULL;
MetaShadow **shadow_location;
gboolean recompute_shadow;
gboolean should_have_shadow;
gboolean appears_focused;
if (!priv->mapped)
return;
/* Calling meta_window_actor_has_shadow() here at every pre-paint is cheap
* and avoids the need to explicitly handle window type changes, which
* we would do if tried to keep track of when we might be adding or removing
* a shadow more explicitly. We only keep track of changes to the *shape* of
* the shadow with priv->recompute_shadow.
*/
should_have_shadow = meta_window_actor_has_shadow (self);
appears_focused = meta_window_appears_focused (priv->window);
if (appears_focused)
{
recompute_shadow = priv->recompute_focused_shadow;
priv->recompute_focused_shadow = FALSE;
shadow_location = &priv->focused_shadow;
}
else
{
recompute_shadow = priv->recompute_unfocused_shadow;
priv->recompute_unfocused_shadow = FALSE;
shadow_location = &priv->unfocused_shadow;
}
if (!should_have_shadow || recompute_shadow)
{
if (*shadow_location != NULL)
{
old_shadow = *shadow_location;
*shadow_location = NULL;
}
}
if (*shadow_location == NULL && should_have_shadow)
{
if (priv->shadow_shape == NULL)
{
if (priv->shaped && priv->shape_region)
priv->shadow_shape = meta_window_shape_new (priv->shape_region);
else if (priv->bounding_region)
priv->shadow_shape = meta_window_shape_new (priv->bounding_region);
}
if (priv->shadow_shape != NULL)
{
MetaShadowFactory *factory = meta_shadow_factory_get_default ();
const char *shadow_class = meta_window_actor_get_shadow_class (self);
cairo_rectangle_int_t shape_bounds;
meta_window_actor_get_shape_bounds (self, &shape_bounds);
*shadow_location = meta_shadow_factory_get_shadow (factory,
priv->shadow_shape,
shape_bounds.width, shape_bounds.height,
shadow_class, appears_focused);
}
}
if (old_shadow != NULL)
meta_shadow_unref (old_shadow);
}
static gboolean
is_frozen (MetaWindowActor *self)
{
@@ -1733,6 +2013,7 @@ check_needs_reshape (MetaWindowActor *self)
#endif
priv->needs_reshape = FALSE;
meta_window_actor_invalidate_shadow (self);
}
void
@@ -1743,6 +2024,11 @@ meta_window_actor_update_shape (MetaWindowActor *self,
priv->shaped = shaped;
priv->needs_reshape = TRUE;
if (priv->shadow_shape != NULL)
{
meta_window_shape_unref (priv->shadow_shape);
priv->shadow_shape = NULL;
}
clutter_actor_queue_redraw (priv->actor);
}
@@ -1770,8 +2056,19 @@ meta_window_actor_pre_paint (MetaWindowActor *self)
priv->received_damage = FALSE;
}
check_needs_reshape (self);
check_needs_pixmap (self);
check_needs_reshape (self);
check_needs_shadow (self);
}
void
meta_window_actor_invalidate_shadow (MetaWindowActor *self)
{
MetaWindowActorPrivate *priv = self->priv;
priv->recompute_focused_shadow = TRUE;
priv->recompute_unfocused_shadow = TRUE;
clutter_actor_queue_redraw (CLUTTER_ACTOR (self));
}
void

View File

@@ -5,8 +5,11 @@
#define _ISOC99_SOURCE /* for roundf */
#include <math.h>
#include <gdk/gdk.h> /* for gdk_rectangle_intersect() */
#include "meta-window-actor-private.h"
#include "meta-window-group.h"
#include "meta-background-actor.h"
struct _MetaWindowGroupClass
{
@@ -102,7 +105,9 @@ meta_window_group_paint (ClutterActor *actor)
{
MetaWindowGroup *window_group = META_WINDOW_GROUP (actor);
cairo_region_t *visible_region;
GLboolean scissor_test;
cairo_rectangle_int_t screen_rect = { 0 };
cairo_rectangle_int_t scissor_rect;
GList *children, *l;
/* We walk the list from top to bottom (opposite of painting order),
@@ -118,35 +123,67 @@ meta_window_group_paint (ClutterActor *actor)
* optimization, however.)
*/
meta_screen_get_size (window_group->screen, &screen_rect.width, &screen_rect.height);
visible_region = cairo_region_create_rectangle (&screen_rect);
/* When doing a partial stage paint, Clutter will set the GL scissor
* box to the clip rectangle for the partial repaint. We combine the screen
* rectangle with the scissor box to get the region we need to
* paint. (Strangely, the scissor box sometimes seems to be bigger
* than the stage ... Clutter should probably be clampimg)
*/
glGetBooleanv (GL_SCISSOR_TEST, &scissor_test);
if (scissor_test)
{
GLint scissor_box[4];
glGetIntegerv (GL_SCISSOR_BOX, scissor_box);
scissor_rect.x = scissor_box[0];
scissor_rect.y = screen_rect.height - (scissor_box[1] + scissor_box[3]);
scissor_rect.width = scissor_box[2];
scissor_rect.height = scissor_box[3];
gdk_rectangle_intersect (&scissor_rect, &screen_rect, &scissor_rect);
}
else
{
scissor_rect = screen_rect;
}
visible_region = cairo_region_create_rectangle (&scissor_rect);
for (l = children; l; l = l->next)
{
MetaWindowActor *window_actor;
gboolean x, y;
if (!META_IS_WINDOW_ACTOR (l->data) || !CLUTTER_ACTOR_IS_VISIBLE (l->data))
if (!CLUTTER_ACTOR_IS_VISIBLE (l->data))
continue;
window_actor = l->data;
if (!actor_is_untransformed (CLUTTER_ACTOR (window_actor), &x, &y))
continue;
/* Temporarily move to the coordinate system of the actor */
cairo_region_translate (visible_region, - x, - y);
meta_window_actor_set_visible_region (window_actor, visible_region);
if (clutter_actor_get_paint_opacity (CLUTTER_ACTOR (window_actor)) == 0xff)
if (META_IS_WINDOW_ACTOR (l->data))
{
cairo_region_t *obscured_region = meta_window_actor_get_obscured_region (window_actor);
if (obscured_region)
cairo_region_subtract (visible_region, obscured_region);
}
MetaWindowActor *window_actor = l->data;
gboolean x, y;
meta_window_actor_set_visible_region_beneath (window_actor, visible_region);
cairo_region_translate (visible_region, x, y);
if (!actor_is_untransformed (CLUTTER_ACTOR (window_actor), &x, &y))
continue;
/* Temporarily move to the coordinate system of the actor */
cairo_region_translate (visible_region, - x, - y);
meta_window_actor_set_visible_region (window_actor, visible_region);
if (clutter_actor_get_paint_opacity (CLUTTER_ACTOR (window_actor)) == 0xff)
{
cairo_region_t *obscured_region = meta_window_actor_get_obscured_region (window_actor);
if (obscured_region)
cairo_region_subtract (visible_region, obscured_region);
}
meta_window_actor_set_visible_region_beneath (window_actor, visible_region);
cairo_region_translate (visible_region, x, y);
}
else if (META_IS_BACKGROUND_ACTOR (l->data))
{
MetaBackgroundActor *background_actor = l->data;
meta_background_actor_set_visible_region (background_actor, visible_region);
}
}
cairo_region_destroy (visible_region);
@@ -158,13 +195,17 @@ meta_window_group_paint (ClutterActor *actor)
*/
for (l = children; l; l = l->next)
{
MetaWindowActor *window_actor;
if (!META_IS_WINDOW_ACTOR (l->data))
continue;
window_actor = l->data;
meta_window_actor_reset_visible_regions (window_actor);
if (META_IS_WINDOW_ACTOR (l->data))
{
MetaWindowActor *window_actor = l->data;
window_actor = l->data;
meta_window_actor_reset_visible_regions (window_actor);
}
else if (META_IS_BACKGROUND_ACTOR (l->data))
{
MetaBackgroundActor *background_actor = l->data;
meta_background_actor_set_visible_region (background_actor, NULL);
}
}
g_list_free (children);

View File

@@ -0,0 +1,254 @@
/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
/*
* MetaWindowShape
*
* Extracted invariant window shape
*
* Copyright (C) 2010 Red Hat, Inc.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License as
* published by the Free Software Foundation; either version 2 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
* 02111-1307, USA.
*/
#include <string.h>
#include "meta-window-shape.h"
#include "region-utils.h"
struct _MetaWindowShape
{
guint ref_count;
int top, right, bottom, left;
int n_rectangles;
cairo_rectangle_int_t *rectangles;
guint hash;
};
MetaWindowShape *
meta_window_shape_new (cairo_region_t *region)
{
MetaWindowShape *shape;
MetaRegionIterator iter;
cairo_rectangle_int_t extents;
int max_yspan_y1 = 0;
int max_yspan_y2 = 0;
int max_xspan_x1 = -1;
int max_xspan_x2 = -1;
guint hash;
shape = g_slice_new0 (MetaWindowShape);
shape->ref_count = 1;
cairo_region_get_extents (region, &extents);
shape->n_rectangles = cairo_region_num_rectangles (region);
if (shape->n_rectangles == 0)
{
shape->rectangles = NULL;
shape->top = shape->right = shape->bottom = shape->left = 0;
shape->hash = 0;
return shape;
}
for (meta_region_iterator_init (&iter, region);
!meta_region_iterator_at_end (&iter);
meta_region_iterator_next (&iter))
{
int max_line_xspan_x1 = -1;
int max_line_xspan_x2 = -1;
if (iter.rectangle.width > max_line_xspan_x2 - max_line_xspan_x1)
{
max_line_xspan_x1 = iter.rectangle.x;
max_line_xspan_x2 = iter.rectangle.x + iter.rectangle.width;
}
if (iter.line_end)
{
if (iter.rectangle.height > max_yspan_y2 - max_yspan_y1)
{
max_yspan_y1 = iter.rectangle.y;
max_yspan_y2 = iter.rectangle.y + iter.rectangle.height;
}
if (max_xspan_x1 < 0) /* First line */
{
max_xspan_x1 = max_line_xspan_x1;
max_xspan_x2 = max_line_xspan_x2;
}
else
{
max_xspan_x1 = MAX (max_xspan_x1, max_line_xspan_x1);
max_xspan_x2 = MIN (max_xspan_x2, max_line_xspan_x2);
if (max_xspan_x2 < max_xspan_x1)
max_xspan_x2 = max_xspan_x1;
}
}
}
#if 0
g_print ("xspan: %d -> %d, yspan: %d -> %d\n",
max_xspan_x1, max_xspan_x2,
max_yspan_y1, max_yspan_y2);
#endif
shape->top = max_yspan_y1 - extents.y;
shape->right = extents.x + extents.width - max_xspan_x2;
shape->bottom = extents.y + extents.height - max_yspan_y2;
shape->left = max_xspan_x1 - extents.x;
shape->rectangles = g_new (cairo_rectangle_int_t, shape->n_rectangles);
hash = 0;
for (meta_region_iterator_init (&iter, region);
!meta_region_iterator_at_end (&iter);
meta_region_iterator_next (&iter))
{
int x1, x2, y1, y2;
x1 = iter.rectangle.x;
x2 = iter.rectangle.x + iter.rectangle.width;
y1 = iter.rectangle.y;
y2 = iter.rectangle.y + iter.rectangle.height;
if (x1 > max_xspan_x1)
x1 -= MIN (x1, max_xspan_x2 - 1) - max_xspan_x1;
if (x2 > max_xspan_x1)
x2 -= MIN (x2, max_xspan_x2 - 1) - max_xspan_x1;
if (y1 > max_yspan_y1)
y1 -= MIN (y1, max_yspan_y2 - 1) - max_yspan_y1;
if (y2 > max_yspan_y1)
y2 -= MIN (y2, max_yspan_y2 - 1) - max_yspan_y1;
shape->rectangles[iter.i].x = x1 - extents.x;
shape->rectangles[iter.i].y = y1 - extents.y;
shape->rectangles[iter.i].width = x2 - x1;
shape->rectangles[iter.i].height = y2 - y1;
#if 0
g_print ("%d: +%d+%dx%dx%d => +%d+%dx%dx%d\n",
iter.i, iter.rectangle.x, iter.rectangle.y, iter.rectangle.width, iter.rectangle.height,
shape->rectangles[iter.i].x, shape->rectangles[iter.i].y,
hape->rectangles[iter.i].width, shape->rectangles[iter.i].height);
#endif
hash = hash * 31 + x1 * 17 + x2 * 27 + y1 * 37 + y2 * 43;
}
shape->hash = hash;
#if 0
g_print ("%d %d %d %d: %#x\n\n", shape->top, shape->right, shape->bottom, shape->left, shape->hash);
#endif
return shape;
}
MetaWindowShape *
meta_window_shape_ref (MetaWindowShape *shape)
{
shape->ref_count++;
return shape;
}
void
meta_window_shape_unref (MetaWindowShape *shape)
{
shape->ref_count--;
if (shape->ref_count == 0)
{
g_free (shape->rectangles);
g_slice_free (MetaWindowShape, shape);
}
}
guint
meta_window_shape_hash (MetaWindowShape *shape)
{
return shape->hash;
}
gboolean
meta_window_shape_equal (MetaWindowShape *shape_a,
MetaWindowShape *shape_b)
{
if (shape_a->n_rectangles != shape_b->n_rectangles)
return FALSE;
return memcmp (shape_a->rectangles, shape_b->rectangles,
sizeof (cairo_rectangle_int_t) * shape_a->n_rectangles) == 0;
}
void
meta_window_shape_get_borders (MetaWindowShape *shape,
int *border_top,
int *border_right,
int *border_bottom,
int *border_left)
{
if (border_top)
*border_top = shape->top;
if (border_right)
*border_right = shape->right;
if (border_bottom)
*border_bottom = shape->bottom;
if (border_left)
*border_left = shape->left;
}
/**
* meta_window_shape_to_region:
* @shape: a #MetaWindowShape
* @center_width: size of the central region horizontally
* @center_height: size of the central region vertically
*
* Converts the shape to to a cairo_region_t using the given width
* and height for the central scaled region.
*
* Return value: a newly created region
*/
cairo_region_t *
meta_window_shape_to_region (MetaWindowShape *shape,
int center_width,
int center_height)
{
cairo_region_t *region;
int i;
region = cairo_region_create ();
for (i = 0; i < shape->n_rectangles; i++)
{
cairo_rectangle_int_t rect = shape->rectangles[i];
if (rect.x <= shape->left && rect.x + rect.width >= shape->left + 1)
rect.width += center_width;
else if (rect.x >= shape->left + 1)
rect.x += center_width;
if (rect.y <= shape->top && rect.y + rect.height >= shape->top + 1)
rect.height += center_height;
else if (rect.y >= shape->top + 1)
rect.y += center_height;
cairo_region_union_rectangle (region, &rect);
}
return region;
}

View File

@@ -0,0 +1,60 @@
/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
/*
* MetaWindowShape
*
* Extracted invariant window shape
*
* Copyright (C) 2010 Red Hat, Inc.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License as
* published by the Free Software Foundation; either version 2 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
* 02111-1307, USA.
*/
#ifndef __META_WINDOW_SHAPE_H__
#define __META_WINDOW_SHAPE_H__
#include <cairo.h>
#include <glib.h>
/**
* MetaWindowShape:
* #MetaWindowShape represents a 9-sliced region with borders on all sides that
* are unscaled, and a constant central region that is scaled. For example,
* the regions representing two windows that are rounded rectangles,
* with the same corner radius but different sizes, have the
* same MetaWindowShape.
*
* #MetaWindowShape is designed to be used as part of a hash table key, so has
* efficient hash and equal functions.
*/
typedef struct _MetaWindowShape MetaWindowShape;
MetaWindowShape * meta_window_shape_new (cairo_region_t *region);
MetaWindowShape * meta_window_shape_ref (MetaWindowShape *shape);
void meta_window_shape_unref (MetaWindowShape *shape);
guint meta_window_shape_hash (MetaWindowShape *shape);
gboolean meta_window_shape_equal (MetaWindowShape *shape_a,
MetaWindowShape *shape_b);
void meta_window_shape_get_borders (MetaWindowShape *shape,
int *border_top,
int *border_right,
int *border_bottom,
int *border_left);
cairo_region_t *meta_window_shape_to_region (MetaWindowShape *shape,
int center_width,
int center_height);
#endif /* __META_WINDOW_SHAPE_H __*/

View File

@@ -0,0 +1,336 @@
/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
/*
* Utilities for region manipulation
*
* Copyright (C) 2010 Red Hat, Inc.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License as
* published by the Free Software Foundation; either version 2 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
* 02111-1307, USA.
*/
#include "region-utils.h"
#include <math.h>
/* MetaRegionBuilder */
/* Various algorithms in this file require unioning together a set of rectangles
* that are unsorted or overlap; unioning such a set of rectangles 1-by-1
* using cairo_region_union_rectangle() produces O(N^2) behavior (if the union
* adds or removes rectangles in the middle of the region, then it has to
* move all the rectangles after that.) To avoid this behavior, MetaRegionBuilder
* creates regions for small groups of rectangles and merges them together in
* a binary tree.
*
* Possible improvement: From a glance at the code, accumulating all the rectangles
* into a flat array and then calling the (not usefully documented)
* cairo_region_create_rectangles() would have the same behavior and would be
* simpler and a bit more efficient.
*/
/* Optimium performance seems to be with MAX_CHUNK_RECTANGLES=4; 8 is about 10% slower.
* But using 8 may be more robust to systems with slow malloc(). */
#define MAX_CHUNK_RECTANGLES 8
#define MAX_LEVELS 16
typedef struct
{
/* To merge regions in binary tree order, we need to keep track of
* the regions that we've already merged together at different
* levels of the tree. We fill in an array in the pattern:
*
* |a |
* |b |a |
* |c | |ab |
* |d |c |ab |
* |e | | |abcd|
*/
cairo_region_t *levels[MAX_LEVELS];
int n_levels;
} MetaRegionBuilder;
static void
meta_region_builder_init (MetaRegionBuilder *builder)
{
int i;
for (i = 0; i < MAX_LEVELS; i++)
builder->levels[i] = NULL;
builder->n_levels = 1;
}
static void
meta_region_builder_add_rectangle (MetaRegionBuilder *builder,
int x,
int y,
int width,
int height)
{
cairo_rectangle_int_t rect;
int i;
if (builder->levels[0] == NULL)
builder->levels[0] = cairo_region_create ();
rect.x = x;
rect.y = y;
rect.width = width;
rect.height = height;
cairo_region_union_rectangle (builder->levels[0], &rect);
if (cairo_region_num_rectangles (builder->levels[0]) >= MAX_CHUNK_RECTANGLES)
{
for (i = 1; i < builder->n_levels + 1; i++)
{
if (builder->levels[i] == NULL)
{
if (i < MAX_LEVELS)
{
builder->levels[i] = builder->levels[i - 1];
builder->levels[i - 1] = NULL;
if (i == builder->n_levels)
builder->n_levels++;
}
break;
}
else
{
cairo_region_union (builder->levels[i], builder->levels[i - 1]);
cairo_region_destroy (builder->levels[i - 1]);
builder->levels[i - 1] = NULL;
}
}
}
}
static cairo_region_t *
meta_region_builder_finish (MetaRegionBuilder *builder)
{
cairo_region_t *result = NULL;
int i;
for (i = 0; i < builder->n_levels; i++)
{
if (builder->levels[i])
{
if (result == NULL)
result = builder->levels[i];
else
{
cairo_region_union(result, builder->levels[i]);
cairo_region_destroy (builder->levels[i]);
}
}
}
if (result == NULL)
result = cairo_region_create ();
return result;
}
/* MetaRegionIterator */
void
meta_region_iterator_init (MetaRegionIterator *iter,
cairo_region_t *region)
{
iter->region = region;
iter->i = 0;
iter->n_rectangles = cairo_region_num_rectangles (region);
iter->line_start = TRUE;
if (iter->n_rectangles > 1)
{
cairo_region_get_rectangle (region, 0, &iter->rectangle);
cairo_region_get_rectangle (region, 1, &iter->next_rectangle);
iter->line_end = iter->next_rectangle.y != iter->rectangle.y;
}
else if (iter->n_rectangles > 0)
{
cairo_region_get_rectangle (region, 0, &iter->rectangle);
iter->line_end = TRUE;
}
}
gboolean
meta_region_iterator_at_end (MetaRegionIterator *iter)
{
return iter->i >= iter->n_rectangles;
}
void
meta_region_iterator_next (MetaRegionIterator *iter)
{
iter->i++;
iter->rectangle = iter->next_rectangle;
iter->line_start = iter->line_end;
if (iter->i + 1 < iter->n_rectangles)
{
cairo_region_get_rectangle (iter->region, iter->i + 1, &iter->next_rectangle);
iter->line_end = iter->next_rectangle.y != iter->rectangle.y;
}
else
{
iter->line_end = TRUE;
}
}
static void
add_expanded_rect (MetaRegionBuilder *builder,
int x,
int y,
int width,
int height,
int x_amount,
int y_amount,
gboolean flip)
{
if (flip)
meta_region_builder_add_rectangle (builder,
y - y_amount, x - x_amount,
height + 2 * y_amount, width + 2 * x_amount);
else
meta_region_builder_add_rectangle (builder,
x - x_amount, y - y_amount,
width + 2 * x_amount, height + 2 * y_amount);
}
static cairo_region_t *
expand_region (cairo_region_t *region,
int x_amount,
int y_amount,
gboolean flip)
{
MetaRegionBuilder builder;
int n;
int i;
meta_region_builder_init (&builder);
n = cairo_region_num_rectangles (region);
for (i = 0; i < n; i++)
{
cairo_rectangle_int_t rect;
cairo_region_get_rectangle (region, i, &rect);
add_expanded_rect (&builder,
rect.x, rect.y, rect.width, rect.height,
x_amount, y_amount, flip);
}
return meta_region_builder_finish (&builder);
}
/* This computes a (clipped version) of the inverse of the region
* and expands it by the given amount */
static cairo_region_t *
expand_region_inverse (cairo_region_t *region,
int x_amount,
int y_amount,
gboolean flip)
{
MetaRegionBuilder builder;
MetaRegionIterator iter;
cairo_rectangle_int_t extents;
cairo_region_t *chunk;
int last_x;
meta_region_builder_init (&builder);
cairo_region_get_extents (region, &extents);
add_expanded_rect (&builder,
extents.x, extents.y - 1, extents.width, 1,
x_amount, y_amount, flip);
add_expanded_rect (&builder,
extents.x - 1, extents.y, 1, extents.height,
x_amount, y_amount, flip);
add_expanded_rect (&builder,
extents.x + extents.width, extents.y, 1, extents.height,
x_amount, y_amount, flip);
add_expanded_rect (&builder,
extents.x, extents.y + extents.height, extents.width, 1,
x_amount, y_amount, flip);
chunk = NULL;
last_x = extents.x;
for (meta_region_iterator_init (&iter, region);
!meta_region_iterator_at_end (&iter);
meta_region_iterator_next (&iter))
{
if (chunk == NULL)
chunk = cairo_region_create ();
if (iter.rectangle.x > last_x)
add_expanded_rect (&builder,
last_x, iter.rectangle.y,
iter.rectangle.x - last_x, iter.rectangle.height,
x_amount, y_amount, flip);
if (iter.line_end)
{
if (extents.x + extents.width > iter.rectangle.x + iter.rectangle.width)
add_expanded_rect (&builder,
iter.rectangle.x + iter.rectangle.width, iter.rectangle.y,
(extents.x + extents.width) - (iter.rectangle.x + iter.rectangle.width), iter.rectangle.height,
x_amount, y_amount, flip);
last_x = extents.x;
}
else
last_x = iter.rectangle.x + iter.rectangle.width;
}
return meta_region_builder_finish (&builder);
}
/**
* meta_make_border_region:
* @region: a #cairo_region_t
* @x_amount: distance from the border to extend horizontally
* @y_amount: distance from the border to extend vertically
* @flip: if true, the result is computed with x and y interchanged
*
* Computes the "border region" of a given region, which is roughly
* speaking the set of points near the boundary of the region. If we
* define the operation of growing a region as computing the set of
* points within a given manhattan distance of the region, then the
* border is 'grow(region) intersect grow(inverse(region))'.
*
* If we create an image by filling the region with a solid color,
* the border is the region affected by blurring the region.
*
* Return value: a new region which is the border of the given region
*/
cairo_region_t *
meta_make_border_region (cairo_region_t *region,
int x_amount,
int y_amount,
gboolean flip)
{
cairo_region_t *border_region;
cairo_region_t *inverse_region;
border_region = expand_region (region, x_amount, y_amount, flip);
inverse_region = expand_region_inverse (region, x_amount, y_amount, flip);
cairo_region_intersect (border_region, inverse_region);
cairo_region_destroy (inverse_region);
return border_region;
}

View File

@@ -0,0 +1,76 @@
/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
/*
* Utilities for region manipulation
*
* Copyright (C) 2010 Red Hat, Inc.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License as
* published by the Free Software Foundation; either version 2 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
* 02111-1307, USA.
*/
#ifndef __META_REGION_UTILS_H__
#define __META_REGION_UTILS_H__
#include <clutter/clutter.h>
#include <cairo.h>
#include <glib.h>
/**
* MetaRegionIterator:
* @region: region being iterated
* @rectangle: current rectangle
* @line_start: whether the current rectangle starts a horizontal band
* @line_end: whether the current rectangle ends a horizontal band
*
* cairo_region_t is a yx banded region; sometimes its useful to iterate through
* such a region treating the start and end of each horizontal band in a distinct
* fashion.
*
* Usage:
*
* MetaRegionIterator iter;
* for (meta_region_iterator_init (&iter, region);
* !meta_region_iterator_at_end (&iter);
* meta_region_iterator_next (&iter))
* {
* [ Use iter.rectangle, iter.line_start, iter.line_end ]
* }
*/
typedef struct _MetaRegionIterator MetaRegionIterator;
struct _MetaRegionIterator {
cairo_region_t *region;
cairo_rectangle_int_t rectangle;
gboolean line_start;
gboolean line_end;
int i;
/*< private >*/
int n_rectangles;
cairo_rectangle_int_t next_rectangle;
};
void meta_region_iterator_init (MetaRegionIterator *iter,
cairo_region_t *region);
gboolean meta_region_iterator_at_end (MetaRegionIterator *iter);
void meta_region_iterator_next (MetaRegionIterator *iter);
cairo_region_t *meta_make_border_region (cairo_region_t *region,
int x_amount,
int y_amount,
gboolean flip);
#endif /* __META_REGION_UTILS_H__ */

View File

@@ -1,350 +0,0 @@
/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
#define _GNU_SOURCE /* For M_PI */
#include <math.h>
#include "compositor-private.h"
#include "shadow.h"
#include "tidy/tidy-texture-frame.h"
#define SHADOW_RADIUS 8
#define SHADOW_OPACITY 0.9
#define SHADOW_OFFSET_X (SHADOW_RADIUS)
#define SHADOW_OFFSET_Y (SHADOW_RADIUS)
#define MAX_TILE_SZ 8 /* Must be <= shaddow radius */
#define TILE_WIDTH (3*MAX_TILE_SZ)
#define TILE_HEIGHT (3*MAX_TILE_SZ)
static unsigned char* shadow_gaussian_make_tile (void);
ClutterActor *
meta_create_shadow_frame (MetaCompositor *compositor)
{
ClutterActor *frame;
if (!compositor->shadow_src)
{
guchar *data;
data = shadow_gaussian_make_tile ();
compositor->shadow_src = clutter_texture_new ();
clutter_texture_set_from_rgb_data (CLUTTER_TEXTURE (compositor->shadow_src),
data,
TRUE,
TILE_WIDTH,
TILE_HEIGHT,
TILE_WIDTH*4,
4,
0,
NULL);
g_free (data);
}
frame = tidy_texture_frame_new (CLUTTER_TEXTURE (compositor->shadow_src),
MAX_TILE_SZ,
MAX_TILE_SZ,
MAX_TILE_SZ,
MAX_TILE_SZ);
clutter_actor_set_position (frame,
SHADOW_OFFSET_X , SHADOW_OFFSET_Y);
return frame;
}
typedef struct GaussianMap
{
int size;
double * data;
} GaussianMap;
static double
gaussian (double r, double x, double y)
{
return ((1 / (sqrt (2 * M_PI * r))) *
exp ((- (x * x + y * y)) / (2 * r * r)));
}
static GaussianMap *
make_gaussian_map (double r)
{
GaussianMap *c;
int size = ((int) ceil ((r * 3)) + 1) & ~1;
int center = size / 2;
int x, y;
double t = 0.0;
double g;
c = g_malloc (sizeof (GaussianMap) + size * size * sizeof (double));
c->size = size;
c->data = (double *) (c + 1);
for (y = 0; y < size; y++)
for (x = 0; x < size; x++)
{
g = gaussian (r, (double) (x - center), (double) (y - center));
t += g;
c->data[y * size + x] = g;
}
for (y = 0; y < size; y++)
for (x = 0; x < size; x++)
c->data[y*size + x] /= t;
return c;
}
static unsigned char
sum_gaussian (GaussianMap * map, double opacity,
int x, int y, int width, int height)
{
int fx, fy;
double * g_data;
double * g_line = map->data;
int g_size = map->size;
int center = g_size / 2;
int fx_start, fx_end;
int fy_start, fy_end;
double v;
unsigned int r;
/*
* Compute set of filter values which are "in range",
* that's the set with:
* 0 <= x + (fx-center) && x + (fx-center) < width &&
* 0 <= y + (fy-center) && y + (fy-center) < height
*
* 0 <= x + (fx - center) x + fx - center < width
* center - x <= fx fx < width + center - x
*/
fx_start = center - x;
if (fx_start < 0)
fx_start = 0;
fx_end = width + center - x;
if (fx_end > g_size)
fx_end = g_size;
fy_start = center - y;
if (fy_start < 0)
fy_start = 0;
fy_end = height + center - y;
if (fy_end > g_size)
fy_end = g_size;
g_line = g_line + fy_start * g_size + fx_start;
v = 0;
for (fy = fy_start; fy < fy_end; fy++)
{
g_data = g_line;
g_line += g_size;
for (fx = fx_start; fx < fx_end; fx++)
v += *g_data++;
}
if (v > 1)
v = 1;
v *= (opacity * 255.0);
r = (unsigned int) v;
return (unsigned char) r;
}
static unsigned char *
shadow_gaussian_make_tile ()
{
unsigned char * data;
int size;
int center;
int x, y;
unsigned char d;
int pwidth, pheight;
double opacity = SHADOW_OPACITY;
static GaussianMap * gaussian_map = NULL;
struct _mypixel
{
unsigned char r;
unsigned char g;
unsigned char b;
unsigned char a;
} * _d;
if (!gaussian_map)
gaussian_map =
make_gaussian_map (SHADOW_RADIUS);
size = gaussian_map->size;
center = size / 2;
/* Top & bottom */
pwidth = MAX_TILE_SZ;
pheight = MAX_TILE_SZ;
data = g_malloc0 (4 * TILE_WIDTH * TILE_HEIGHT);
_d = (struct _mypixel*) data;
/* N */
for (y = 0; y < pheight; y++)
{
d = sum_gaussian (gaussian_map, opacity,
center, y - center,
TILE_WIDTH, TILE_HEIGHT);
for (x = 0; x < pwidth; x++)
{
_d[y*3*pwidth + x + pwidth].r = 0;
_d[y*3*pwidth + x + pwidth].g = 0;
_d[y*3*pwidth + x + pwidth].b = 0;
_d[y*3*pwidth + x + pwidth].a = d;
}
}
/* S */
pwidth = MAX_TILE_SZ;
pheight = MAX_TILE_SZ;
for (y = 0; y < pheight; y++)
{
d = sum_gaussian (gaussian_map, opacity,
center, y - center,
TILE_WIDTH, TILE_HEIGHT);
for (x = 0; x < pwidth; x++)
{
_d[(pheight-y-1)*3*pwidth + 6*pwidth*pheight + x + pwidth].r = 0;
_d[(pheight-y-1)*3*pwidth + 6*pwidth*pheight + x + pwidth].g = 0;
_d[(pheight-y-1)*3*pwidth + 6*pwidth*pheight + x + pwidth].b = 0;
_d[(pheight-y-1)*3*pwidth + 6*pwidth*pheight + x + pwidth].a = d;
}
}
/* w */
pwidth = MAX_TILE_SZ;
pheight = MAX_TILE_SZ;
for (x = 0; x < pwidth; x++)
{
d = sum_gaussian (gaussian_map, opacity,
x - center, center,
TILE_WIDTH, TILE_HEIGHT);
for (y = 0; y < pheight; y++)
{
_d[y*3*pwidth + 3*pwidth*pheight + x].r = 0;
_d[y*3*pwidth + 3*pwidth*pheight + x].g = 0;
_d[y*3*pwidth + 3*pwidth*pheight + x].b = 0;
_d[y*3*pwidth + 3*pwidth*pheight + x].a = d;
}
}
/* E */
for (x = 0; x < pwidth; x++)
{
d = sum_gaussian (gaussian_map, opacity,
x - center, center,
TILE_WIDTH, TILE_HEIGHT);
for (y = 0; y < pheight; y++)
{
_d[y*3*pwidth + 3*pwidth*pheight + (pwidth-x-1) + 2*pwidth].r = 0;
_d[y*3*pwidth + 3*pwidth*pheight + (pwidth-x-1) + 2*pwidth].g = 0;
_d[y*3*pwidth + 3*pwidth*pheight + (pwidth-x-1) + 2*pwidth].b = 0;
_d[y*3*pwidth + 3*pwidth*pheight + (pwidth-x-1) + 2*pwidth].a = d;
}
}
/* NW */
pwidth = MAX_TILE_SZ;
pheight = MAX_TILE_SZ;
for (x = 0; x < pwidth; x++)
for (y = 0; y < pheight; y++)
{
d = sum_gaussian (gaussian_map, opacity,
x-center, y-center,
TILE_WIDTH, TILE_HEIGHT);
_d[y*3*pwidth + x].r = 0;
_d[y*3*pwidth + x].g = 0;
_d[y*3*pwidth + x].b = 0;
_d[y*3*pwidth + x].a = d;
}
/* SW */
for (x = 0; x < pwidth; x++)
for (y = 0; y < pheight; y++)
{
d = sum_gaussian (gaussian_map, opacity,
x-center, y-center,
TILE_WIDTH, TILE_HEIGHT);
_d[(pheight-y-1)*3*pwidth + 6*pwidth*pheight + x].r = 0;
_d[(pheight-y-1)*3*pwidth + 6*pwidth*pheight + x].g = 0;
_d[(pheight-y-1)*3*pwidth + 6*pwidth*pheight + x].b = 0;
_d[(pheight-y-1)*3*pwidth + 6*pwidth*pheight + x].a = d;
}
/* SE */
for (x = 0; x < pwidth; x++)
for (y = 0; y < pheight; y++)
{
d = sum_gaussian (gaussian_map, opacity,
x-center, y-center,
TILE_WIDTH, TILE_HEIGHT);
_d[(pheight-y-1)*3*pwidth + 6*pwidth*pheight + (pwidth-x-1) +
2*pwidth].r = 0;
_d[(pheight-y-1)*3*pwidth + 6*pwidth*pheight + (pwidth-x-1) +
2*pwidth].g = 0;
_d[(pheight-y-1)*3*pwidth + 6*pwidth*pheight + (pwidth-x-1) +
2*pwidth].b = 0;
_d[(pheight-y-1)*3*pwidth + 6*pwidth*pheight + (pwidth-x-1) +
2*pwidth].a = d;
}
/* NE */
for (x = 0; x < pwidth; x++)
for (y = 0; y < pheight; y++)
{
d = sum_gaussian (gaussian_map, opacity,
x-center, y-center,
TILE_WIDTH, TILE_HEIGHT);
_d[y*3*pwidth + (pwidth - x - 1) + 2*pwidth].r = 0;
_d[y*3*pwidth + (pwidth - x - 1) + 2*pwidth].g = 0;
_d[y*3*pwidth + (pwidth - x - 1) + 2*pwidth].b = 0;
_d[y*3*pwidth + (pwidth - x - 1) + 2*pwidth].a = d;
}
/* center */
pwidth = MAX_TILE_SZ;
pheight = MAX_TILE_SZ;
d = sum_gaussian (gaussian_map, opacity,
center, center, TILE_WIDTH, TILE_HEIGHT);
for (x = 0; x < pwidth; x++)
for (y = 0; y < pheight; y++)
{
_d[y*3*pwidth + 3*pwidth*pheight + x + pwidth].r = 0;
_d[y*3*pwidth + 3*pwidth*pheight + x + pwidth].g = 0;
_d[y*3*pwidth + 3*pwidth*pheight + x + pwidth].b = 0;
_d[y*3*pwidth + 3*pwidth*pheight + x + pwidth].a = 0;
}
return data;
}

View File

@@ -1,9 +0,0 @@
#ifndef SHADOW_H
#define SHADOW_H
#include <clutter/clutter.h>
#include "compositor.h"
ClutterActor *meta_create_shadow_frame (MetaCompositor *compositor);
#endif /* SHADOW_H */

View File

@@ -1,641 +0,0 @@
/* tidy-texture-frame.h: Expandible texture actor
*
* Copyright (C) 2007 OpenedHand
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the
* Free Software Foundation, Inc., 59 Temple Place - Suite 330,
* Boston, MA 02111-1307, USA.
*/
/**
* SECTION:tidy-texture-frame
* @short_description: Stretch a texture to fit the entire allocation
*
* #TidyTextureFrame
*
*/
#include <cogl/cogl.h>
#include "tidy-texture-frame.h"
#define TIDY_PARAM_READABLE \
(G_PARAM_READABLE | \
G_PARAM_STATIC_NICK | G_PARAM_STATIC_NAME | G_PARAM_STATIC_BLURB)
#define TIDY_PARAM_READWRITE \
(G_PARAM_READABLE | G_PARAM_WRITABLE | \
G_PARAM_STATIC_NICK | G_PARAM_STATIC_NAME | G_PARAM_STATIC_BLURB)
enum
{
PROP_0,
PROP_PARENT_TEXTURE,
PROP_LEFT,
PROP_TOP,
PROP_RIGHT,
PROP_BOTTOM
};
G_DEFINE_TYPE (TidyTextureFrame, tidy_texture_frame, CLUTTER_TYPE_ACTOR);
#define TIDY_TEXTURE_FRAME_GET_PRIVATE(obj) (G_TYPE_INSTANCE_GET_PRIVATE ((obj), TIDY_TYPE_TEXTURE_FRAME, TidyTextureFramePrivate))
struct _TidyTextureFramePrivate
{
ClutterTexture *parent_texture;
gfloat left;
gfloat top;
gfloat right;
gfloat bottom;
CoglHandle material;
guint needs_paint : 1;
};
static void
tidy_texture_frame_get_preferred_width (ClutterActor *self,
gfloat for_height,
gfloat *min_width_p,
gfloat *natural_width_p)
{
TidyTextureFramePrivate *priv = TIDY_TEXTURE_FRAME (self)->priv;
if (G_UNLIKELY (priv->parent_texture == NULL))
{
if (min_width_p)
*min_width_p = 0;
if (natural_width_p)
*natural_width_p = 0;
}
else
{
ClutterActorClass *klass;
/* by directly querying the parent texture's class implementation
* we are going around any override mechanism the parent texture
* might have in place, and we ask directly for the original
* preferred width
*/
klass = CLUTTER_ACTOR_GET_CLASS (priv->parent_texture);
klass->get_preferred_width (CLUTTER_ACTOR (priv->parent_texture),
for_height,
min_width_p,
natural_width_p);
}
}
static void
tidy_texture_frame_get_preferred_height (ClutterActor *self,
gfloat for_width,
gfloat *min_height_p,
gfloat *natural_height_p)
{
TidyTextureFramePrivate *priv = TIDY_TEXTURE_FRAME (self)->priv;
if (G_UNLIKELY (priv->parent_texture == NULL))
{
if (min_height_p)
*min_height_p = 0;
if (natural_height_p)
*natural_height_p = 0;
}
else
{
ClutterActorClass *klass;
/* by directly querying the parent texture's class implementation
* we are going around any override mechanism the parent texture
* might have in place, and we ask directly for the original
* preferred height
*/
klass = CLUTTER_ACTOR_GET_CLASS (priv->parent_texture);
klass->get_preferred_height (CLUTTER_ACTOR (priv->parent_texture),
for_width,
min_height_p,
natural_height_p);
}
}
static void
tidy_texture_frame_realize (ClutterActor *self)
{
TidyTextureFramePrivate *priv = TIDY_TEXTURE_FRAME (self)->priv;
if (priv->material != COGL_INVALID_HANDLE)
return;
priv->material = cogl_material_new ();
CLUTTER_ACTOR_SET_FLAGS (self, CLUTTER_ACTOR_REALIZED);
}
static void
tidy_texture_frame_unrealize (ClutterActor *self)
{
TidyTextureFramePrivate *priv = TIDY_TEXTURE_FRAME (self)->priv;
if (priv->material == COGL_INVALID_HANDLE)
return;
cogl_handle_unref (priv->material);
priv->material = COGL_INVALID_HANDLE;
CLUTTER_ACTOR_UNSET_FLAGS (self, CLUTTER_ACTOR_REALIZED);
}
static void
tidy_texture_frame_paint (ClutterActor *self)
{
TidyTextureFramePrivate *priv = TIDY_TEXTURE_FRAME (self)->priv;
CoglHandle cogl_texture = COGL_INVALID_HANDLE;
ClutterActorBox box = { 0, };
gfloat width, height;
gfloat tex_width, tex_height;
gfloat ex, ey;
gfloat tx1, ty1, tx2, ty2;
guint8 opacity;
/* no need to paint stuff if we don't have a texture */
if (G_UNLIKELY (priv->parent_texture == NULL))
return;
if (!priv->needs_paint)
return;
/* parent texture may have been hidden, so need to make sure it gets
* realized
*/
if (!CLUTTER_ACTOR_IS_REALIZED (priv->parent_texture))
clutter_actor_realize (CLUTTER_ACTOR (priv->parent_texture));
cogl_texture = clutter_texture_get_cogl_texture (priv->parent_texture);
if (cogl_texture == COGL_INVALID_HANDLE)
return;
tex_width = cogl_texture_get_width (cogl_texture);
tex_height = cogl_texture_get_height (cogl_texture);
clutter_actor_get_allocation_box (self, &box);
width = box.x2 - box.x1;
height = box.y2 - box.y1;
tx1 = priv->left / tex_width;
tx2 = (tex_width - priv->right) / tex_width;
ty1 = priv->top / tex_height;
ty2 = (tex_height - priv->bottom) / tex_height;
ex = width - priv->right;
if (ex < 0)
ex = priv->right; /* FIXME ? */
ey = height - priv->bottom;
if (ey < 0)
ey = priv->bottom; /* FIXME ? */
opacity = clutter_actor_get_paint_opacity (self);
g_assert (priv->material != COGL_INVALID_HANDLE);
/* set the source material using the parent texture's COGL handle */
cogl_material_set_color4ub (priv->material, opacity, opacity, opacity, opacity);
cogl_material_set_layer (priv->material, 0, cogl_texture);
cogl_set_source (priv->material);
/* top left corner */
cogl_rectangle_with_texture_coords (0, 0, priv->left, priv->top,
0.0, 0.0,
tx1, ty1);
/* top middle */
cogl_rectangle_with_texture_coords (priv->left, 0, ex, priv->top,
tx1, 0.0,
tx2, ty1);
/* top right */
cogl_rectangle_with_texture_coords (ex, 0, width, priv->top,
tx2, 0.0,
1.0, ty1);
/* mid left */
cogl_rectangle_with_texture_coords (0, priv->top, priv->left, ey,
0.0, ty1,
tx1, ty2);
/* center */
cogl_rectangle_with_texture_coords (priv->left, priv->top, ex, ey,
tx1, ty1,
tx2, ty2);
/* mid right */
cogl_rectangle_with_texture_coords (ex, priv->top, width, ey,
tx2, ty1,
1.0, ty2);
/* bottom left */
cogl_rectangle_with_texture_coords (0, ey, priv->left, height,
0.0, ty2,
tx1, 1.0);
/* bottom center */
cogl_rectangle_with_texture_coords (priv->left, ey, ex, height,
tx1, ty2,
tx2, 1.0);
/* bottom right */
cogl_rectangle_with_texture_coords (ex, ey, width, height,
tx2, ty2,
1.0, 1.0);
}
static inline void
tidy_texture_frame_set_frame_internal (TidyTextureFrame *frame,
gfloat left,
gfloat top,
gfloat right,
gfloat bottom)
{
TidyTextureFramePrivate *priv = frame->priv;
GObject *gobject = G_OBJECT (frame);
gboolean changed = FALSE;
g_object_freeze_notify (gobject);
if (priv->top != top)
{
priv->top = top;
g_object_notify (gobject, "top");
changed = TRUE;
}
if (priv->right != right)
{
priv->right = right;
g_object_notify (gobject, "right");
changed = TRUE;
}
if (priv->bottom != bottom)
{
priv->bottom = bottom;
g_object_notify (gobject, "bottom");
changed = TRUE;
}
if (priv->left != left)
{
priv->left = left;
g_object_notify (gobject, "left");
changed = TRUE;
}
if (changed && CLUTTER_ACTOR_IS_VISIBLE (frame))
clutter_actor_queue_redraw (CLUTTER_ACTOR (frame));
g_object_thaw_notify (gobject);
}
static void
tidy_texture_frame_set_property (GObject *gobject,
guint prop_id,
const GValue *value,
GParamSpec *pspec)
{
TidyTextureFrame *frame = TIDY_TEXTURE_FRAME (gobject);
TidyTextureFramePrivate *priv = frame->priv;
switch (prop_id)
{
case PROP_PARENT_TEXTURE:
tidy_texture_frame_set_parent_texture (frame,
g_value_get_object (value));
break;
case PROP_TOP:
tidy_texture_frame_set_frame_internal (frame,
priv->left,
g_value_get_float (value),
priv->right,
priv->bottom);
break;
case PROP_RIGHT:
tidy_texture_frame_set_frame_internal (frame,
priv->top,
g_value_get_float (value),
priv->bottom,
priv->left);
break;
case PROP_BOTTOM:
tidy_texture_frame_set_frame_internal (frame,
priv->top,
priv->right,
g_value_get_float (value),
priv->left);
break;
case PROP_LEFT:
tidy_texture_frame_set_frame_internal (frame,
priv->top,
priv->right,
priv->bottom,
g_value_get_float (value));
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (gobject, prop_id, pspec);
break;
}
}
static void
tidy_texture_frame_get_property (GObject *gobject,
guint prop_id,
GValue *value,
GParamSpec *pspec)
{
TidyTextureFramePrivate *priv = TIDY_TEXTURE_FRAME (gobject)->priv;
switch (prop_id)
{
case PROP_PARENT_TEXTURE:
g_value_set_object (value, priv->parent_texture);
break;
case PROP_LEFT:
g_value_set_float (value, priv->left);
break;
case PROP_TOP:
g_value_set_float (value, priv->top);
break;
case PROP_RIGHT:
g_value_set_float (value, priv->right);
break;
case PROP_BOTTOM:
g_value_set_float (value, priv->bottom);
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (gobject, prop_id, pspec);
break;
}
}
static void
tidy_texture_frame_dispose (GObject *gobject)
{
TidyTextureFramePrivate *priv = TIDY_TEXTURE_FRAME (gobject)->priv;
if (priv->parent_texture)
{
g_object_unref (priv->parent_texture);
priv->parent_texture = NULL;
}
if (priv->material)
{
cogl_handle_unref (priv->material);
priv->material = COGL_INVALID_HANDLE;
}
G_OBJECT_CLASS (tidy_texture_frame_parent_class)->dispose (gobject);
}
static void
tidy_texture_frame_class_init (TidyTextureFrameClass *klass)
{
GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
ClutterActorClass *actor_class = CLUTTER_ACTOR_CLASS (klass);
GParamSpec *pspec;
g_type_class_add_private (gobject_class, sizeof (TidyTextureFramePrivate));
actor_class->get_preferred_width =
tidy_texture_frame_get_preferred_width;
actor_class->get_preferred_height =
tidy_texture_frame_get_preferred_height;
actor_class->realize = tidy_texture_frame_realize;
actor_class->unrealize = tidy_texture_frame_unrealize;
actor_class->paint = tidy_texture_frame_paint;
gobject_class->set_property = tidy_texture_frame_set_property;
gobject_class->get_property = tidy_texture_frame_get_property;
gobject_class->dispose = tidy_texture_frame_dispose;
pspec = g_param_spec_object ("parent-texture",
"Parent Texture",
"The parent ClutterTexture",
CLUTTER_TYPE_TEXTURE,
TIDY_PARAM_READWRITE |
G_PARAM_CONSTRUCT);
g_object_class_install_property (gobject_class, PROP_PARENT_TEXTURE, pspec);
pspec = g_param_spec_float ("left",
"Left",
"Left offset",
0, G_MAXFLOAT,
0,
TIDY_PARAM_READWRITE);
g_object_class_install_property (gobject_class, PROP_LEFT, pspec);
pspec = g_param_spec_float ("top",
"Top",
"Top offset",
0, G_MAXFLOAT,
0,
TIDY_PARAM_READWRITE);
g_object_class_install_property (gobject_class, PROP_TOP, pspec);
pspec = g_param_spec_float ("bottom",
"Bottom",
"Bottom offset",
0, G_MAXFLOAT,
0,
TIDY_PARAM_READWRITE);
g_object_class_install_property (gobject_class, PROP_BOTTOM, pspec);
pspec = g_param_spec_float ("right",
"Right",
"Right offset",
0, G_MAXFLOAT,
0,
TIDY_PARAM_READWRITE);
g_object_class_install_property (gobject_class, PROP_RIGHT, pspec);
}
static void
tidy_texture_frame_init (TidyTextureFrame *self)
{
TidyTextureFramePrivate *priv;
self->priv = priv = TIDY_TEXTURE_FRAME_GET_PRIVATE (self);
priv->material = COGL_INVALID_HANDLE;
}
/**
* tidy_texture_frame_new:
* @texture: a #ClutterTexture or %NULL
* @left: left margin preserving its content
* @top: top margin preserving its content
* @right: right margin preserving its content
* @bottom: bottom margin preserving its content
*
* A #TidyTextureFrame is a specialized texture that efficiently clones
* an area of the given @texture while keeping preserving portions of the
* same texture.
*
* A #TidyTextureFrame can be used to make a rectangular texture fit a
* given size without stretching its borders.
*
* Return value: the newly created #TidyTextureFrame
*/
ClutterActor*
tidy_texture_frame_new (ClutterTexture *texture,
gfloat left,
gfloat top,
gfloat right,
gfloat bottom)
{
g_return_val_if_fail (texture == NULL || CLUTTER_IS_TEXTURE (texture), NULL);
return g_object_new (TIDY_TYPE_TEXTURE_FRAME,
"parent-texture", texture,
"left", left,
"top", top,
"right", right,
"bottom", bottom,
NULL);
}
ClutterTexture *
tidy_texture_frame_get_parent_texture (TidyTextureFrame *frame)
{
g_return_val_if_fail (TIDY_IS_TEXTURE_FRAME (frame), NULL);
return frame->priv->parent_texture;
}
void
tidy_texture_frame_set_parent_texture (TidyTextureFrame *frame,
ClutterTexture *texture)
{
TidyTextureFramePrivate *priv;
gboolean was_visible;
g_return_if_fail (TIDY_IS_TEXTURE_FRAME (frame));
g_return_if_fail (texture == NULL || CLUTTER_IS_TEXTURE (texture));
priv = frame->priv;
was_visible = CLUTTER_ACTOR_IS_VISIBLE (frame);
if (priv->parent_texture == texture)
return;
if (priv->parent_texture)
{
g_object_unref (priv->parent_texture);
priv->parent_texture = NULL;
if (was_visible)
clutter_actor_hide (CLUTTER_ACTOR (frame));
}
if (texture)
{
priv->parent_texture = g_object_ref (texture);
if (was_visible && CLUTTER_ACTOR_IS_VISIBLE (priv->parent_texture))
clutter_actor_show (CLUTTER_ACTOR (frame));
}
clutter_actor_queue_relayout (CLUTTER_ACTOR (frame));
g_object_notify (G_OBJECT (frame), "parent-texture");
}
void
tidy_texture_frame_set_frame (TidyTextureFrame *frame,
gfloat top,
gfloat right,
gfloat bottom,
gfloat left)
{
g_return_if_fail (TIDY_IS_TEXTURE_FRAME (frame));
tidy_texture_frame_set_frame_internal (frame, top, right, bottom, left);
}
void
tidy_texture_frame_get_frame (TidyTextureFrame *frame,
gfloat *top,
gfloat *right,
gfloat *bottom,
gfloat *left)
{
TidyTextureFramePrivate *priv;
g_return_if_fail (TIDY_IS_TEXTURE_FRAME (frame));
priv = frame->priv;
if (top)
*top = priv->top;
if (right)
*right = priv->right;
if (bottom)
*bottom = priv->bottom;
if (left)
*left = priv->left;
}
/**
* tidy_texture_frame_set_needs_paint:
* @frame: a #TidyTextureframe
* @needs_paint: if %FALSE, the paint will be skipped
*
* Provides a hint to the texture frame that it is totally obscured
* and doesn't need to be painted. This would typically be called
* by a parent container if it detects the condition prior to
* painting its children and then unset afterwards.
*
* Since it is not supposed to have any effect on display, it does
* not queue a repaint.
*/
void
tidy_texture_frame_set_needs_paint (TidyTextureFrame *frame,
gboolean needs_paint)
{
TidyTextureFramePrivate *priv;
g_return_if_fail (TIDY_IS_TEXTURE_FRAME (frame));
priv = frame->priv;
priv->needs_paint = needs_paint;
}

View File

@@ -1,84 +0,0 @@
/* tidy-texture-frame.h: Expandible texture actor
*
* Copyright (C) 2007, 2008 OpenedHand Ltd
* Copyright (C) 2009 Intel Corp.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the
* Free Software Foundation, Inc., 59 Temple Place - Suite 330,
* Boston, MA 02111-1307, USA.
*/
#ifndef _HAVE_TIDY_TEXTURE_FRAME_H
#define _HAVE_TIDY_TEXTURE_FRAME_H
#include <clutter/clutter.h>
G_BEGIN_DECLS
#define TIDY_TYPE_TEXTURE_FRAME (tidy_texture_frame_get_type ())
#define TIDY_TEXTURE_FRAME(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), TIDY_TYPE_TEXTURE_FRAME, TidyTextureFrame))
#define TIDY_TEXTURE_FRAME_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), TIDY_TYPE_TEXTURE_FRAME, TidyTextureFrameClass))
#define TIDY_IS_TEXTURE_FRAME(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), TIDY_TYPE_TEXTURE_FRAME))
#define TIDY_IS_TEXTURE_FRAME_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), TIDY_TYPE_TEXTURE_FRAME))
#define TIDY_TEXTURE_FRAME_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), TIDY_TYPE_TEXTURE_FRAME, TidyTextureFrameClass))
typedef struct _TidyTextureFrame TidyTextureFrame;
typedef struct _TidyTextureFramePrivate TidyTextureFramePrivate;
typedef struct _TidyTextureFrameClass TidyTextureFrameClass;
struct _TidyTextureFrame
{
/*< private >*/
ClutterActor parent_instance;
TidyTextureFramePrivate *priv;
};
struct _TidyTextureFrameClass
{
ClutterActorClass parent_class;
/* padding for future expansion */
void (*_clutter_box_1) (void);
void (*_clutter_box_2) (void);
void (*_clutter_box_3) (void);
void (*_clutter_box_4) (void);
};
GType tidy_texture_frame_get_type (void) G_GNUC_CONST;
ClutterActor * tidy_texture_frame_new (ClutterTexture *texture,
gfloat top,
gfloat right,
gfloat bottom,
gfloat left);
void tidy_texture_frame_set_parent_texture (TidyTextureFrame *frame,
ClutterTexture *texture);
ClutterTexture *tidy_texture_frame_get_parent_texture (TidyTextureFrame *frame);
void tidy_texture_frame_set_frame (TidyTextureFrame *frame,
gfloat top,
gfloat right,
gfloat bottom,
gfloat left);
void tidy_texture_frame_get_frame (TidyTextureFrame *frame,
gfloat *top,
gfloat *right,
gfloat *bottom,
gfloat *left);
void tidy_texture_frame_set_needs_paint (TidyTextureFrame *frame,
gboolean needs_paint);
G_END_DECLS
#endif /* _HAVE_TIDY_TEXTURE_FRAME_H */

View File

@@ -796,7 +796,7 @@ constrain_maximization (MetaWindow *window,
/* Determine whether constraint applies; exit if it doesn't */
if ((!window->maximized_horizontally && !window->maximized_vertically) ||
META_WINDOW_TILED (window))
META_WINDOW_TILED_SIDE_BY_SIDE (window))
return TRUE;
/* Calculate target_size = maximized size of (window + frame) */
@@ -880,7 +880,7 @@ constrain_tiling (MetaWindow *window,
return TRUE;
/* Determine whether constraint applies; exit if it doesn't */
if (!META_WINDOW_TILED (window))
if (!META_WINDOW_TILED_SIDE_BY_SIDE (window))
return TRUE;
/* Calculate target_size - as the tile previews need this as well, we
@@ -968,7 +968,8 @@ constrain_size_increments (MetaWindow *window,
/* Determine whether constraint applies; exit if it doesn't */
if (META_WINDOW_MAXIMIZED (window) || window->fullscreen ||
META_WINDOW_TILED (window) || info->action_type == ACTION_MOVE)
META_WINDOW_TILED_SIDE_BY_SIDE (window) ||
info->action_type == ACTION_MOVE)
return TRUE;
/* Determine whether constraint is already satisfied; exit if it is */
@@ -1099,7 +1100,8 @@ constrain_aspect_ratio (MetaWindow *window,
constraints_are_inconsistent = minr > maxr;
if (constraints_are_inconsistent ||
META_WINDOW_MAXIMIZED (window) || window->fullscreen ||
META_WINDOW_TILED (window) || info->action_type == ACTION_MOVE)
META_WINDOW_TILED_SIDE_BY_SIDE (window) ||
info->action_type == ACTION_MOVE)
return TRUE;
/* Determine whether constraint is already satisfied; exit if it is. We

View File

@@ -117,65 +117,8 @@ meta_core_get (Display *xdisplay,
*((MetaFrameFlags*)answer) = meta_frame_get_flags (window->frame);
break;
case META_CORE_GET_FRAME_TYPE:
{
MetaFrameType base_type = META_FRAME_TYPE_LAST;
switch (window->type)
{
case META_WINDOW_NORMAL:
base_type = META_FRAME_TYPE_NORMAL;
break;
case META_WINDOW_DIALOG:
base_type = META_FRAME_TYPE_DIALOG;
break;
case META_WINDOW_MODAL_DIALOG:
base_type = META_FRAME_TYPE_MODAL_DIALOG;
break;
case META_WINDOW_MENU:
base_type = META_FRAME_TYPE_MENU;
break;
case META_WINDOW_UTILITY:
base_type = META_FRAME_TYPE_UTILITY;
break;
case META_WINDOW_DESKTOP:
case META_WINDOW_DOCK:
case META_WINDOW_TOOLBAR:
case META_WINDOW_SPLASHSCREEN:
case META_WINDOW_DROPDOWN_MENU:
case META_WINDOW_POPUP_MENU:
case META_WINDOW_TOOLTIP:
case META_WINDOW_NOTIFICATION:
case META_WINDOW_COMBO:
case META_WINDOW_DND:
case META_WINDOW_OVERRIDE_OTHER:
/* No frame */
base_type = META_FRAME_TYPE_LAST;
break;
}
if (base_type == META_FRAME_TYPE_LAST)
{
/* can't add border if undecorated */
*((MetaFrameType*)answer) = META_FRAME_TYPE_LAST;
}
else if (window->border_only)
{
/* override base frame type */
*((MetaFrameType*)answer) = META_FRAME_TYPE_BORDER;
}
else
{
*((MetaFrameType*)answer) = base_type;
}
break;
}
*((MetaFrameType*)answer) = meta_window_get_frame_type (window);
break;
case META_CORE_GET_MINI_ICON:
*((GdkPixbuf**)answer) = window->mini_icon;
break;

View File

@@ -2642,6 +2642,10 @@ event_callback (XEvent *event,
meta_bell_notify (display, xkb_ev);
}
break;
case XkbNewKeyboardNotify:
case XkbMapNotify:
meta_display_process_mapping_event (display, event);
break;
}
}
#endif

View File

@@ -233,9 +233,8 @@ reload_keycodes (MetaDisplay *display)
{
meta_topic (META_DEBUG_KEYBINDINGS,
"Reloading keycodes for binding tables\n");
if (display->overlay_key_combo.keysym
&& display->overlay_key_combo.keycode == 0)
if (display->overlay_key_combo.keysym != 0)
{
display->overlay_key_combo.keycode = XKeysymToKeycode (
display->xdisplay, display->overlay_key_combo.keysym);
@@ -248,9 +247,11 @@ reload_keycodes (MetaDisplay *display)
i = 0;
while (i < display->n_key_bindings)
{
if (display->key_bindings[i].keycode == 0)
if (display->key_bindings[i].keysym != 0)
{
display->key_bindings[i].keycode = XKeysymToKeycode (
display->xdisplay, display->key_bindings[i].keysym);
}
++i;
}
@@ -530,26 +531,51 @@ void
meta_display_process_mapping_event (MetaDisplay *display,
XEvent *event)
{
gboolean keymap_changed = FALSE;
gboolean modmap_changed = FALSE;
#ifdef HAVE_XKB
if (event->type == display->xkb_base_event_type)
{
meta_topic (META_DEBUG_KEYBINDINGS,
"XKB mapping changed, will redo keybindings\n");
keymap_changed = TRUE;
modmap_changed = TRUE;
}
else
#endif
if (event->xmapping.request == MappingModifier)
{
meta_topic (META_DEBUG_KEYBINDINGS,
"Received MappingModifier event, will reload modmap and redo keybindings\n");
reload_modmap (display);
reload_modifiers (display);
regrab_key_bindings (display);
modmap_changed = TRUE;
}
else if (event->xmapping.request == MappingKeyboard)
{
meta_topic (META_DEBUG_KEYBINDINGS,
"Received MappingKeyboard event, will reload keycodes and redo keybindings\n");
reload_keymap (display);
keymap_changed = TRUE;
}
/* Now to do the work itself */
if (keymap_changed || modmap_changed)
{
if (keymap_changed)
reload_keymap (display);
/* Deciphering the modmap depends on the loaded keysyms to find out
* what modifiers is Super and so forth, so we need to reload it
* even when only the keymap changes */
reload_modmap (display);
reload_keycodes (display);
if (keymap_changed)
reload_keycodes (display);
reload_modifiers (display);
regrab_key_bindings (display);
}
@@ -617,6 +643,14 @@ meta_display_init_keys (MetaDisplay *display)
/* Keys are actually grabbed in meta_screen_grab_keys() */
meta_prefs_add_listener (bindings_changed_callback, display);
#ifdef HAVE_XKB
/* meta_display_init_keys() should have already called XkbQueryExtension() */
if (display->xkb_base_event_type != -1)
XkbSelectEvents (display->xdisplay, XkbUseCoreKbd,
XkbNewKeyboardNotifyMask | XkbMapNotifyMask,
XkbNewKeyboardNotifyMask | XkbMapNotifyMask);
#endif
}
void

View File

@@ -104,7 +104,7 @@ static char *cursor_theme = NULL;
static int cursor_size = 24;
static gboolean compositing_manager = FALSE;
static gboolean resize_with_right_button = FALSE;
static gboolean side_by_side_tiling = FALSE;
static gboolean edge_tiling = FALSE;
static gboolean force_fullscreen = TRUE;
static MetaVisualBellType visual_bell_type = META_VISUAL_BELL_FULLSCREEN_FLASH;
@@ -423,9 +423,9 @@ static MetaBoolPreference preferences_bool[] =
&resize_with_right_button,
FALSE,
},
{ "/apps/metacity/general/side_by_side_tiling",
META_PREF_SIDE_BY_SIDE_TILING,
&side_by_side_tiling,
{ "/apps/metacity/general/edge_tiling",
META_PREF_EDGE_TILING,
&edge_tiling,
FALSE,
},
{ "/apps/mutter/general/live_hidden_windows",
@@ -2010,8 +2010,8 @@ meta_preference_to_string (MetaPreference pref)
case META_PREF_RESIZE_WITH_RIGHT_BUTTON:
return "RESIZE_WITH_RIGHT_BUTTON";
case META_PREF_SIDE_BY_SIDE_TILING:
return "SIDE_BY_SIDE_TILING";
case META_PREF_EDGE_TILING:
return "EDGE_TILING";
case META_PREF_FORCE_FULLSCREEN:
return "FORCE_FULLSCREEN";
@@ -2926,9 +2926,9 @@ meta_prefs_get_gnome_animations ()
}
gboolean
meta_prefs_get_side_by_side_tiling ()
meta_prefs_get_edge_tiling ()
{
return side_by_side_tiling;
return edge_tiling;
}
MetaKeyBindingAction

View File

@@ -1758,6 +1758,7 @@ meta_screen_tile_preview_update_timeout (gpointer data)
MetaScreen *screen = data;
MetaWindow *window = screen->display->grab_window;
gboolean composited = screen->display->compositor != NULL;
gboolean needs_preview = FALSE;
screen->tile_preview_timeout_id = 0;
@@ -1775,9 +1776,28 @@ meta_screen_tile_preview_update_timeout (gpointer data)
create_serial);
}
if (window
&& !META_WINDOW_TILED (window)
&& window->tile_mode != META_TILE_NONE)
if (window)
{
switch (window->tile_mode)
{
case META_TILE_LEFT:
case META_TILE_RIGHT:
if (!META_WINDOW_TILED_SIDE_BY_SIDE (window))
needs_preview = TRUE;
break;
case META_TILE_MAXIMIZED:
if (!META_WINDOW_MAXIMIZED (window))
needs_preview = TRUE;
break;
default:
needs_preview = FALSE;
break;
}
}
if (needs_preview)
{
MetaRectangle tile_rect;

View File

@@ -54,8 +54,8 @@
*
* When we receive a new event: a) we compare the serial in the event to
* the serial of the queued requests and remove any that are now
* no longer pending b) drop the predicted stacking order to recompute
* it at the next opportunity.
* no longer pending b) if necessary, drop the predicted stacking
* order to recompute it at the next opportunity.
*
* Possible optimizations:
* Keep the stacks as an array + reverse-mapping hash table to avoid
@@ -505,6 +505,8 @@ static void
stack_tracker_event_received (MetaStackTracker *tracker,
MetaStackOp *op)
{
gboolean need_sync = FALSE;
meta_stack_op_dump (op, "Stack op event received: ", "\n");
if (op->any.serial < tracker->server_serial)
@@ -512,7 +514,8 @@ stack_tracker_event_received (MetaStackTracker *tracker,
tracker->server_serial = op->any.serial;
meta_stack_op_apply (op, tracker->server_stack);
if (meta_stack_op_apply (op, tracker->server_stack))
need_sync = TRUE;
while (tracker->queued_requests->head)
{
@@ -522,17 +525,21 @@ stack_tracker_event_received (MetaStackTracker *tracker,
g_queue_pop_head (tracker->queued_requests);
meta_stack_op_free (queued_op);
need_sync = TRUE;
}
if (tracker->predicted_stack)
if (need_sync)
{
g_array_free (tracker->predicted_stack, TRUE);
tracker->predicted_stack = NULL;
if (tracker->predicted_stack)
{
g_array_free (tracker->predicted_stack, TRUE);
tracker->predicted_stack = NULL;
}
meta_stack_tracker_queue_sync_stack (tracker);
}
meta_stack_tracker_dump (tracker);
meta_stack_tracker_queue_sync_stack (tracker);
}
void

View File

@@ -64,7 +64,8 @@ typedef enum {
typedef enum {
META_TILE_NONE,
META_TILE_LEFT,
META_TILE_RIGHT
META_TILE_RIGHT,
META_TILE_MAXIMIZED
} MetaTileMode;
struct _MetaWindow
@@ -409,11 +410,11 @@ struct _MetaWindowClass
(w)->maximized_vertically)
#define META_WINDOW_MAXIMIZED_VERTICALLY(w) ((w)->maximized_vertically)
#define META_WINDOW_MAXIMIZED_HORIZONTALLY(w) ((w)->maximized_horizontally)
#define META_WINDOW_TILED(w) ((w)->maximized_vertically && \
!(w)->maximized_horizontally && \
(w)->tile_mode != META_TILE_NONE)
#define META_WINDOW_TILED_SIDE_BY_SIDE(w) ((w)->maximized_vertically && \
!(w)->maximized_horizontally && \
(w)->tile_mode != META_TILE_NONE)
#define META_WINDOW_ALLOWS_MOVE(w) ((w)->has_move_func && !(w)->fullscreen)
#define META_WINDOW_ALLOWS_RESIZE_EXCEPT_HINTS(w) ((w)->has_resize_func && !META_WINDOW_MAXIMIZED (w) && !META_WINDOW_TILED(w) && !(w)->fullscreen && !(w)->shaded)
#define META_WINDOW_ALLOWS_RESIZE_EXCEPT_HINTS(w) ((w)->has_resize_func && !META_WINDOW_MAXIMIZED (w) && !META_WINDOW_TILED_SIDE_BY_SIDE(w) && !(w)->fullscreen && !(w)->shaded)
#define META_WINDOW_ALLOWS_RESIZE(w) (META_WINDOW_ALLOWS_RESIZE_EXCEPT_HINTS (w) && \
(((w)->size_hints.min_width < (w)->size_hints.max_width) || \
((w)->size_hints.min_height < (w)->size_hints.max_height)))
@@ -461,8 +462,6 @@ void meta_window_update_fullscreen_monitors (MetaWindow *window,
unsigned long left,
unsigned long right);
gboolean meta_window_appears_focused (MetaWindow *window);
/* args to move are window pos, not frame pos */
void meta_window_move (MetaWindow *window,
gboolean user_op,

View File

@@ -2972,7 +2972,7 @@ ensure_size_hints_satisfied (MetaRectangle *rect,
static void
meta_window_save_rect (MetaWindow *window)
{
if (!(META_WINDOW_MAXIMIZED (window) || META_WINDOW_TILED (window) || window->fullscreen))
if (!(META_WINDOW_MAXIMIZED (window) || META_WINDOW_TILED_SIDE_BY_SIDE (window) || window->fullscreen))
{
/* save size/pos as appropriate args for move_resize */
if (!window->maximized_horizontally)
@@ -3014,7 +3014,7 @@ force_save_user_window_placement (MetaWindow *window)
static void
save_user_window_placement (MetaWindow *window)
{
if (!(META_WINDOW_MAXIMIZED (window) || META_WINDOW_TILED (window) || window->fullscreen))
if (!(META_WINDOW_MAXIMIZED (window) || META_WINDOW_TILED_SIDE_BY_SIDE (window) || window->fullscreen))
{
MetaRectangle user_rect;
@@ -3158,14 +3158,47 @@ meta_window_maximize (MetaWindow *window,
}
}
/**
* meta_window_get_maximized:
*
* Gets the current maximization state of the window, as combination
* of the %META_MAXIMIZE_HORIZONTAL and %META_MAXIMIZE_VERTICAL flags;
*
* Return value: current maximization state
*/
MetaMaximizeFlags
meta_window_get_maximized (MetaWindow *window)
{
return ((window->maximized_horizontally ? META_MAXIMIZE_HORIZONTAL : 0) |
(window->maximized_vertically ? META_MAXIMIZE_VERTICAL : 0));
}
/**
* meta_window_is_fullscreen:
*
* Return value: %TRUE if the window is currently fullscreen
*/
gboolean
meta_window_is_fullscreen (MetaWindow *window)
{
return window->fullscreen;
}
static void
meta_window_tile (MetaWindow *window)
{
MetaMaximizeFlags directions;
/* Don't do anything if no tiling is requested */
if (window->tile_mode == META_TILE_NONE)
return;
meta_window_maximize_internal (window, META_MAXIMIZE_VERTICAL, NULL);
if (window->tile_mode == META_TILE_MAXIMIZED)
directions = META_MAXIMIZE_VERTICAL | META_MAXIMIZE_HORIZONTAL;
else
directions = META_MAXIMIZE_VERTICAL;
meta_window_maximize_internal (window, directions, NULL);
meta_screen_tile_preview_update (window->screen, FALSE);
if (window->display->compositor)
@@ -3192,7 +3225,7 @@ meta_window_tile (MetaWindow *window)
}
static gboolean
meta_window_can_tile (MetaWindow *window)
meta_window_can_tile_side_by_side (MetaWindow *window)
{
const MetaMonitorInfo *monitor;
MetaRectangle tile_area;
@@ -3223,6 +3256,18 @@ meta_window_can_tile (MetaWindow *window)
tile_area.height >= window->size_hints.min_height;
}
static gboolean
meta_window_can_tile_maximized (MetaWindow *window)
{
if (!META_WINDOW_ALLOWS_RESIZE (window))
return FALSE;
if (!window->has_maximize_func)
return FALSE;
return TRUE;
}
static void
unmaximize_window_before_freeing (MetaWindow *window)
{
@@ -3290,6 +3335,13 @@ meta_window_unmaximize_internal (MetaWindow *window,
window->maximized_vertically =
window->maximized_vertically && !unmaximize_vertically;
/* Reset the tile mode for maximized tiled windows for consistency
* with "normal" maximized windows, but keep other tile modes,
* as side-by-side tiled windows may snap back.
*/
if (window->tile_mode == META_TILE_MAXIMIZED)
window->tile_mode = META_TILE_NONE;
/* Unmaximize to the saved_rect position in the direction(s)
* being unmaximized.
*/
@@ -3373,7 +3425,8 @@ meta_window_unmaximize (MetaWindow *window,
MetaMaximizeFlags directions)
{
/* Restore tiling if necessary */
if (window->tile_mode != META_TILE_NONE)
if (window->tile_mode == META_TILE_LEFT ||
window->tile_mode == META_TILE_RIGHT)
{
window->maximized_horizontally = FALSE;
meta_window_tile (window);
@@ -4665,6 +4718,13 @@ meta_window_get_geometry (MetaWindow *window,
window->size_hints.height_inc;
}
/**
* meta_window_get_outer_rect:
* @window: a #MetaWindow
* @rect: (out): pointer to an allocated #MetaRectangle
*
* Gets the rectangle that bounds @window and, if decorated, its decorations.
*/
void
meta_window_get_outer_rect (const MetaWindow *window,
MetaRectangle *rect)
@@ -7766,14 +7826,13 @@ update_move (MetaWindow *window,
shake_threshold = meta_ui_get_drag_threshold (window->screen->ui) *
DRAG_THRESHOLD_TO_SHAKE_THRESHOLD_FACTOR;
if (meta_prefs_get_side_by_side_tiling () &&
meta_window_can_tile (window))
if (meta_prefs_get_edge_tiling ())
{
const MetaMonitorInfo *monitor;
MetaRectangle work_area;
/* For tiling we are interested in the work area of the monitor where
* the pointer is located.
/* For side-by-side tiling we are interested in the inside vertical
* edges of the work area of the monitor where the pointer is located.
* Also see comment in meta_window_get_current_tile_area()
*/
monitor = meta_screen_get_current_monitor (window->screen);
@@ -7781,17 +7840,38 @@ update_move (MetaWindow *window,
monitor->number,
&work_area);
if (y >= monitor->rect.y &&
y < (monitor->rect.y + monitor->rect.height))
if (meta_window_can_tile_side_by_side (window))
{
/* check if cursor is near an edge of the work area */
if (x >= monitor->rect.x && x < (work_area.x + shake_threshold))
window->tile_mode = META_TILE_LEFT;
else if (x >= work_area.x + work_area.width - shake_threshold &&
x < (monitor->rect.x + monitor->rect.width))
window->tile_mode = META_TILE_RIGHT;
else
window->tile_mode = META_TILE_NONE;
if (y >= monitor->rect.y &&
y < (monitor->rect.y + monitor->rect.height))
{
/* check if cursor is near an edge of the work area */
if (x >= monitor->rect.x && x < (work_area.x + shake_threshold))
window->tile_mode = META_TILE_LEFT;
else if (x >= work_area.x + work_area.width - shake_threshold &&
x < (monitor->rect.x + monitor->rect.width))
window->tile_mode = META_TILE_RIGHT;
else
window->tile_mode = META_TILE_NONE;
}
}
/* For maximized tiling we are interested in the outside top edge
* of the work area of the monitor where the pointer is located.
*
* We use the outside edge instead of the inside edge, because we
* don't want to force users to maximize windows they are placing
* near the top of their screens.
*/
if (meta_window_can_tile_maximized (window))
{
if (x >= monitor->rect.x &&
x < (monitor->rect.x + monitor->rect.width))
{
/* check if cursor is on the top edge of the monitor*/
if (y >= monitor->rect.y && y <= work_area.y)
window->tile_mode = META_TILE_MAXIMIZED;
}
}
}
@@ -7801,12 +7881,15 @@ update_move (MetaWindow *window,
*/
if ((META_WINDOW_MAXIMIZED (window) && ABS (dy) >= shake_threshold) ||
(META_WINDOW_TILED (window) && (MAX (ABS (dx), ABS (dy)) >= shake_threshold)))
(META_WINDOW_TILED_SIDE_BY_SIDE (window) && (MAX (ABS (dx), ABS (dy)) >= shake_threshold)))
{
double prop;
/* Shake loose */
window->shaken_loose = !META_WINDOW_TILED (window);
/* Shake loose, so that the window snaps back to maximized
* when dragged near the top; do not snap back if tiling
* is enabled, as top edge tiling can be used in that case
*/
window->shaken_loose = !meta_prefs_get_edge_tiling ();
window->tile_mode = META_TILE_NONE;
/* move the unmaximized window to the cursor */
@@ -7839,12 +7922,13 @@ update_move (MetaWindow *window,
* loose or it is still maximized (then move straight)
*/
else if ((window->shaken_loose || META_WINDOW_MAXIMIZED (window)) &&
window->tile_mode == META_TILE_NONE)
window->tile_mode != META_TILE_LEFT && window->tile_mode != META_TILE_RIGHT)
{
const MetaMonitorInfo *wmonitor;
MetaRectangle work_area;
int monitor;
window->tile_mode = META_TILE_NONE;
wmonitor = meta_screen_get_monitor_for_window (window->screen, window);
for (monitor = 0; monitor < window->screen->n_monitor_infos; monitor++)
@@ -7903,7 +7987,7 @@ update_move (MetaWindow *window,
meta_window_get_client_root_coords (window, &old);
/* Don't allow movement in the maximized directions or while tiled */
if (window->maximized_horizontally || META_WINDOW_TILED (window))
if (window->maximized_horizontally || META_WINDOW_TILED_SIDE_BY_SIDE (window))
new_x = old.x;
if (window->maximized_vertically)
new_y = old.y;
@@ -8339,6 +8423,23 @@ check_use_this_motion_notify (MetaWindow *window,
}
}
static void
update_tile_mode (MetaWindow *window)
{
switch (window->tile_mode)
{
case META_TILE_LEFT:
case META_TILE_RIGHT:
if (!META_WINDOW_TILED_SIDE_BY_SIDE (window))
window->tile_mode = META_TILE_NONE;
break;
case META_TILE_MAXIMIZED:
if (!META_WINDOW_MAXIMIZED (window))
window->tile_mode = META_TILE_NONE;
break;
}
}
void
meta_window_handle_mouse_grab_op_event (MetaWindow *window,
XEvent *event)
@@ -8431,9 +8532,7 @@ meta_window_handle_mouse_grab_op_event (MetaWindow *window,
* would break the ability to snap back to the tiled
* state, so we wait until mouse release.
*/
if (!window->maximized_vertically &&
window->tile_mode != META_TILE_NONE)
window->tile_mode = META_TILE_NONE;
update_tile_mode (window);
}
}
@@ -9275,9 +9374,22 @@ transient_has_focus (MetaWindow *window,
return FALSE;
}
/**
* meta_window_appears_focused:
* @window: a #MetaWindow
*
* Determines if the window should be drawn with a focused appearance. This is
* true for focused windows but also true for windows with a focused modal
* dialog attached.
*
* Return value: %TRUE if the window should be drawn with a focused frame
*/
gboolean
meta_window_appears_focused (MetaWindow *window)
{
/* FIXME: meta_window_foreach_transient() iterates over all windows; we
* should eat the complexity to cache a bit for this.
*/
if (!window->has_focus && meta_prefs_get_attach_modal_dialogs ())
{
gboolean focus = FALSE;
@@ -9655,3 +9767,74 @@ meta_window_get_mutter_hints (MetaWindow *window)
return window->mutter_hints;
}
/**
* meta_window_get_frame_type:
* @window: a #MetaWindow
*
* Gets the type of window decorations that should be used for this window.
*
* Return value: the frame type
*/
MetaFrameType
meta_window_get_frame_type (MetaWindow *window)
{
MetaFrameType base_type = META_FRAME_TYPE_LAST;
switch (window->type)
{
case META_WINDOW_NORMAL:
base_type = META_FRAME_TYPE_NORMAL;
break;
case META_WINDOW_DIALOG:
base_type = META_FRAME_TYPE_DIALOG;
break;
case META_WINDOW_MODAL_DIALOG:
if (meta_prefs_get_attach_modal_dialogs () &&
meta_window_get_transient_for (window) != NULL)
base_type = META_FRAME_TYPE_ATTACHED;
else
base_type = META_FRAME_TYPE_MODAL_DIALOG;
break;
case META_WINDOW_MENU:
base_type = META_FRAME_TYPE_MENU;
break;
case META_WINDOW_UTILITY:
base_type = META_FRAME_TYPE_UTILITY;
break;
case META_WINDOW_DESKTOP:
case META_WINDOW_DOCK:
case META_WINDOW_TOOLBAR:
case META_WINDOW_SPLASHSCREEN:
case META_WINDOW_DROPDOWN_MENU:
case META_WINDOW_POPUP_MENU:
case META_WINDOW_TOOLTIP:
case META_WINDOW_NOTIFICATION:
case META_WINDOW_COMBO:
case META_WINDOW_DND:
case META_WINDOW_OVERRIDE_OTHER:
/* No frame */
base_type = META_FRAME_TYPE_LAST;
break;
}
if (base_type == META_FRAME_TYPE_LAST)
{
/* can't add border if undecorated */
return META_FRAME_TYPE_LAST;
}
else if (window->border_only && base_type != META_FRAME_TYPE_ATTACHED)
{
/* override base frame type */
return META_FRAME_TYPE_BORDER;
}
else
{
return base_type;
}
}

View File

@@ -618,10 +618,20 @@ meta_workspace_activate_with_focus (MetaWorkspace *workspace,
meta_screen_calc_workspace_layout (workspace->screen, num_workspaces,
new_space, &layout2);
if (layout1.current_col < layout2.current_col)
direction = META_MOTION_RIGHT;
if (layout1.current_col > layout2.current_col)
direction = META_MOTION_LEFT;
if (meta_ui_get_direction() == META_UI_DIRECTION_RTL)
{
if (layout1.current_col > layout2.current_col)
direction = META_MOTION_RIGHT;
else if (layout1.current_col < layout2.current_col)
direction = META_MOTION_LEFT;
}
else
{
if (layout1.current_col < layout2.current_col)
direction = META_MOTION_RIGHT;
else if (layout1.current_col > layout2.current_col)
direction = META_MOTION_LEFT;
}
if (layout1.current_row < layout2.current_row)
{

View File

@@ -195,6 +195,7 @@ typedef enum
META_FRAME_TYPE_UTILITY,
META_FRAME_TYPE_MENU,
META_FRAME_TYPE_BORDER,
META_FRAME_TYPE_ATTACHED,
META_FRAME_TYPE_LAST
} MetaFrameType;

View File

@@ -39,4 +39,6 @@ Window meta_get_overlay_window (MetaScreen *screen);
GList *meta_get_window_actors (MetaScreen *screen);
ClutterActor *meta_get_window_group_for_screen (MetaScreen *screen);
ClutterActor *meta_get_background_actor_for_screen (MetaScreen *screen);
#endif

View File

@@ -249,6 +249,9 @@ meta_plugin_get_overlay_group (MetaPlugin *plugin);
ClutterActor *
meta_plugin_get_window_group (MetaPlugin *plugin);
ClutterActor *
meta_plugin_get_background_actor (MetaPlugin *plugin);
ClutterActor *
meta_plugin_get_stage (MetaPlugin *plugin);

View File

@@ -0,0 +1,84 @@
/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
/*
* MetaShadowFactory:
*
* Create and cache shadow textures for arbitrary window shapes
*
* Copyright (C) 2010 Red Hat, Inc.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License as
* published by the Free Software Foundation; either version 2 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
* 02111-1307, USA.
*/
#ifndef __META_SHADOW_FACTORY_H__
#define __META_SHADOW_FACTORY_H__
#include <glib-object.h>
/**
* MetaShadowParams:
* The #MetaShadowParams structure holds information about how to draw
* a particular style of shadow.
* @radius: the radius (gaussian standard deviation) of the shadow
* @top_fade: if >= 0, the shadow doesn't extend above the top
* of the shape, and fades out over the given number of pixels
* @x_offset: horizontal offset of the shadow with respect to the
* shape being shadowed, in pixels
* @y_offset: vertical offset of the shadow with respect to the
* shape being shadowed, in pixels
* @opacity: opacity of the shadow, from 0 to 255
*/
typedef struct _MetaShadowParams MetaShadowParams;
struct _MetaShadowParams
{
int radius;
int top_fade;
int x_offset;
int y_offset;
guint8 opacity;
};
#define META_TYPE_SHADOW_FACTORY (meta_shadow_factory_get_type ())
#define META_SHADOW_FACTORY(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), META_TYPE_SHADOW_FACTORY, MetaShadowFactory))
#define META_SHADOW_FACTORY_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), META_TYPE_SHADOW_FACTORY, MetaShadowFactoryClass))
#define META_IS_SHADOW_FACTORY(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), META_TYPE_SHADOW_FACTORY))
#define META_IS_SHADOW_FACTORY_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), META_TYPE_SHADOW_FACTORY))
#define META_SHADOW_FACTORY_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), META_TYPE_SHADOW_FACTORY, MetaShadowFactoryClass))
/**
* MetaShadowFactory:
* #MetaShadowFactory is used to create window shadows. It caches shadows internally
* so that multiple shadows created for the same shape with the same radius will
* share the same MetaShadow.
*/
typedef struct _MetaShadowFactory MetaShadowFactory;
typedef struct _MetaShadowFactoryClass MetaShadowFactoryClass;
MetaShadowFactory *meta_shadow_factory_get_default (void);
GType meta_shadow_factory_get_type (void);
void meta_shadow_factory_set_params (MetaShadowFactory *factory,
const char *class_name,
gboolean focused,
MetaShadowParams *params);
void meta_shadow_factory_get_params (MetaShadowFactory *factory,
const char *class_name,
gboolean focused,
MetaShadowParams *params);
#endif /* __META_SHADOW_FACTORY_H__ */

View File

@@ -60,7 +60,7 @@ typedef enum
META_PREF_CURSOR_SIZE,
META_PREF_COMPOSITING_MANAGER,
META_PREF_RESIZE_WITH_RIGHT_BUTTON,
META_PREF_SIDE_BY_SIDE_TILING,
META_PREF_EDGE_TILING,
META_PREF_FORCE_FULLSCREEN,
META_PREF_CLUTTER_PLUGINS,
META_PREF_LIVE_HIDDEN_WINDOWS,
@@ -99,7 +99,7 @@ gboolean meta_prefs_get_auto_raise (void);
int meta_prefs_get_auto_raise_delay (void);
gboolean meta_prefs_get_gnome_accessibility (void);
gboolean meta_prefs_get_gnome_animations (void);
gboolean meta_prefs_get_side_by_side_tiling (void);
gboolean meta_prefs_get_edge_tiling (void);
const char* meta_prefs_get_command (int i);

View File

@@ -28,6 +28,8 @@
#include <glib.h>
#include <glib-object.h>
#include "common.h"
gboolean meta_is_verbose (void);
void meta_set_verbose (gboolean setting);
gboolean meta_is_debugging (void);
@@ -91,6 +93,7 @@ guint meta_unsigned_long_hash (gconstpointer v);
void meta_print_backtrace (void);
const char* meta_frame_type_to_string (MetaFrameType type);
const char* meta_gravity_to_string (int gravity);
#include <libintl.h>

View File

@@ -69,6 +69,7 @@ GType meta_window_get_type (void);
MetaFrame *meta_window_get_frame (MetaWindow *window);
gboolean meta_window_has_focus (MetaWindow *window);
gboolean meta_window_appears_focused (MetaWindow *window);
gboolean meta_window_is_shaded (MetaWindow *window);
gboolean meta_window_is_override_redirect (MetaWindow *window);
gboolean meta_window_is_skip_taskbar (MetaWindow *window);
@@ -119,6 +120,9 @@ void meta_window_foreach_ancestor (MetaWindow *window,
MetaWindowForeachFunc func,
void *user_data);
MetaMaximizeFlags meta_window_get_maximized (MetaWindow *window);
gboolean meta_window_is_fullscreen (MetaWindow *window);
gboolean meta_window_is_mapped (MetaWindow *window);
gboolean meta_window_toplevel_is_mapped (MetaWindow *window);
gboolean meta_window_get_icon_geometry (MetaWindow *window,
@@ -143,4 +147,7 @@ const char *meta_window_get_client_machine (MetaWindow *window);
gboolean meta_window_is_remote (MetaWindow *window);
gboolean meta_window_is_modal (MetaWindow *window);
const char *meta_window_get_mutter_hints (MetaWindow *window);
MetaFrameType meta_window_get_frame_type (MetaWindow *window);
#endif

View File

@@ -51,7 +51,7 @@ set_gdk_window_struts (GdkWindow *window,
vals[11] = 800;
XChangeProperty (GDK_WINDOW_XDISPLAY (window),
GDK_WINDOW_XWINDOW (window),
GDK_WINDOW_XID (window),
XInternAtom (GDK_WINDOW_XDISPLAY (window),
"_NET_WM_STRUT_PARTIAL", False),
XA_CARDINAL, 32, PropModeReplace,
@@ -125,7 +125,7 @@ set_gdk_window_type (GdkWindow *window,
type, False);
XChangeProperty (GDK_WINDOW_XDISPLAY (window),
GDK_WINDOW_XWINDOW (window),
GDK_WINDOW_XID (window),
XInternAtom (GDK_WINDOW_XDISPLAY (window), "_NET_WM_WINDOW_TYPE", False),
XA_ATOM, 32, PropModeReplace,
(guchar *)atoms,

View File

@@ -401,10 +401,10 @@ meta_window_menu_new (MetaFrames *frames,
n_workspaces, active_workspace);
window = gtk_widget_get_window (GTK_WIDGET (frames));
display = gdk_x11_drawable_get_xdisplay (window);
display = GDK_WINDOW_XDISPLAY (window);
screen = gdk_window_get_screen (window);
xroot = GDK_DRAWABLE_XID (gdk_screen_get_root_window (screen));
xroot = GDK_WINDOW_XID (gdk_screen_get_root_window (screen));
submenu = gtk_menu_new ();

View File

@@ -23,19 +23,17 @@
#include "gradient.h"
#include <gtk/gtk.h>
typedef void (* RenderGradientFunc) (GdkDrawable *drawable,
cairo_t *cr,
typedef void (* RenderGradientFunc) (cairo_t *cr,
int width,
int height);
static void
draw_checkerboard (GdkDrawable *drawable,
int width,
int height)
draw_checkerboard (cairo_t *cr,
int width,
int height)
{
gint i, j, xcount, ycount;
GdkColor color1, color2;
cairo_t *cr;
#define CHECK_SIZE 10
#define SPACING 2
@@ -48,8 +46,6 @@ draw_checkerboard (GdkDrawable *drawable,
color2.green = 50000;
color2.blue = 50000;
cr = gdk_cairo_create (drawable);
xcount = 0;
i = SPACING;
while (i < width)
@@ -77,13 +73,10 @@ draw_checkerboard (GdkDrawable *drawable,
i += CHECK_SIZE + SPACING;
++xcount;
}
cairo_destroy (cr);
}
static void
render_simple (GdkDrawable *drawable,
cairo_t *cr,
render_simple (cairo_t *cr,
int width, int height,
MetaGradientType type,
gboolean with_alpha)
@@ -115,7 +108,7 @@ render_simple (GdkDrawable *drawable,
alphas, G_N_ELEMENTS (alphas),
META_GRADIENT_HORIZONTAL);
draw_checkerboard (drawable, width, height);
draw_checkerboard (cr , width, height);
}
gdk_cairo_set_source_pixbuf (cr, pixbuf, 0, 0);
@@ -126,40 +119,35 @@ render_simple (GdkDrawable *drawable,
}
static void
render_vertical_func (GdkDrawable *drawable,
cairo_t *cr,
render_vertical_func (cairo_t *cr,
int width, int height)
{
render_simple (drawable, cr, width, height, META_GRADIENT_VERTICAL, FALSE);
render_simple (cr, width, height, META_GRADIENT_VERTICAL, FALSE);
}
static void
render_horizontal_func (GdkDrawable *drawable,
cairo_t *cr,
render_horizontal_func (cairo_t *cr,
int width, int height)
{
render_simple (drawable, cr, width, height, META_GRADIENT_HORIZONTAL, FALSE);
render_simple (cr, width, height, META_GRADIENT_HORIZONTAL, FALSE);
}
static void
render_diagonal_func (GdkDrawable *drawable,
cairo_t *cr,
render_diagonal_func (cairo_t *cr,
int width, int height)
{
render_simple (drawable, cr, width, height, META_GRADIENT_DIAGONAL, FALSE);
render_simple (cr, width, height, META_GRADIENT_DIAGONAL, FALSE);
}
static void
render_diagonal_alpha_func (GdkDrawable *drawable,
cairo_t *cr,
render_diagonal_alpha_func (cairo_t *cr,
int width, int height)
{
render_simple (drawable, cr, width, height, META_GRADIENT_DIAGONAL, TRUE);
render_simple (cr, width, height, META_GRADIENT_DIAGONAL, TRUE);
}
static void
render_multi (GdkDrawable *drawable,
cairo_t *cr,
render_multi (cairo_t *cr,
int width, int height,
MetaGradientType type)
{
@@ -186,32 +174,28 @@ render_multi (GdkDrawable *drawable,
}
static void
render_vertical_multi_func (GdkDrawable *drawable,
cairo_t *cr,
render_vertical_multi_func (cairo_t *cr,
int width, int height)
{
render_multi (drawable, cr, width, height, META_GRADIENT_VERTICAL);
render_multi (cr, width, height, META_GRADIENT_VERTICAL);
}
static void
render_horizontal_multi_func (GdkDrawable *drawable,
cairo_t *cr,
render_horizontal_multi_func (cairo_t *cr,
int width, int height)
{
render_multi (drawable, cr, width, height, META_GRADIENT_HORIZONTAL);
render_multi (cr, width, height, META_GRADIENT_HORIZONTAL);
}
static void
render_diagonal_multi_func (GdkDrawable *drawable,
cairo_t *cr,
render_diagonal_multi_func (cairo_t *cr,
int width, int height)
{
render_multi (drawable, cr, width, height, META_GRADIENT_DIAGONAL);
render_multi (cr, width, height, META_GRADIENT_DIAGONAL);
}
static void
render_interwoven_func (GdkDrawable *drawable,
cairo_t *cr,
render_interwoven_func (cairo_t *cr,
int width, int height)
{
GdkPixbuf *pixbuf;
@@ -235,31 +219,22 @@ render_interwoven_func (GdkDrawable *drawable,
}
static gboolean
expose_callback (GtkWidget *widget,
GdkEventExpose *event,
gpointer data)
draw_callback (GtkWidget *widget,
cairo_t *cr,
gpointer data)
{
RenderGradientFunc func = data;
GdkWindow *window;
GtkAllocation allocation;
GtkStyle *style;
cairo_t *cr;
style = gtk_widget_get_style (widget);
gtk_widget_get_allocation (widget, &allocation);
window = gtk_widget_get_window (widget);
cr = gdk_cairo_create (window);
gdk_cairo_set_source_color (cr, &style->fg[gtk_widget_get_state (widget)]);
(* func) (gtk_widget_get_window (widget),
cr,
allocation.width,
allocation.height);
(* func) (cr,
gtk_widget_get_allocated_width (widget),
gtk_widget_get_allocated_height (widget));
cairo_destroy (cr);
return TRUE;
return FALSE;
}
static GtkWidget*
@@ -280,8 +255,8 @@ create_gradient_window (const char *title,
gtk_window_set_default_size (GTK_WINDOW (window), 175, 175);
g_signal_connect (G_OBJECT (drawing_area),
"expose_event",
G_CALLBACK (expose_callback),
"draw",
G_CALLBACK (draw_callback),
func);
gtk_container_add (GTK_CONTAINER (window), drawing_area);

View File

@@ -38,7 +38,7 @@
* look out for.
*/
#define THEME_MAJOR_VERSION 3
#define THEME_MINOR_VERSION 1
#define THEME_MINOR_VERSION 2
#define THEME_VERSION (1000 * THEME_MAJOR_VERSION + THEME_MINOR_VERSION)
#define METACITY_THEME_FILENAME_FORMAT "metacity-theme-%d.xml"
@@ -1257,7 +1257,8 @@ parse_toplevel_element (GMarkupParseContext *context,
type = meta_frame_type_from_string (type_name);
if (type == META_FRAME_TYPE_LAST)
if (type == META_FRAME_TYPE_LAST ||
(type == META_FRAME_TYPE_ATTACHED && peek_required_version (info) < 3002))
{
set_error (error, context, G_MARKUP_ERROR, G_MARKUP_ERROR_PARSE,
_("Unknown type \"%s\" on <%s> element"),

View File

@@ -1189,7 +1189,6 @@ const char* meta_frame_resize_to_string (MetaFrameResize r
MetaFrameFocus meta_frame_focus_from_string (const char *str);
const char* meta_frame_focus_to_string (MetaFrameFocus focus);
MetaFrameType meta_frame_type_from_string (const char *str);
const char* meta_frame_type_to_string (MetaFrameType type);
MetaGradientType meta_gradient_type_from_string (const char *str);
const char* meta_gradient_type_to_string (MetaGradientType type);
GtkStateType meta_gtk_state_from_string (const char *str);

View File

@@ -234,7 +234,7 @@ dialog_contents (void)
vbox = gtk_vbox_new (FALSE, 0);
action_area = gtk_hbutton_box_new ();
action_area = gtk_button_box_new (GTK_ORIENTATION_HORIZONTAL);
gtk_button_box_set_layout (GTK_BUTTON_BOX (action_area),
GTK_BUTTONBOX_END);
@@ -407,6 +407,10 @@ get_window_contents (MetaFrameType type,
case META_FRAME_TYPE_BORDER:
*title = _("Border");
return border_only_contents ();
case META_FRAME_TYPE_ATTACHED:
*title = _("Attached Modal Dialog");
return dialog_contents ();
case META_FRAME_TYPE_LAST:
g_assert_not_reached ();
@@ -454,6 +458,9 @@ get_window_flags (MetaFrameType type)
case META_FRAME_TYPE_BORDER:
break;
case META_FRAME_TYPE_ATTACHED:
break;
case META_FRAME_TYPE_LAST:
g_assert_not_reached ();

View File

@@ -4992,7 +4992,7 @@ meta_theme_validate (MetaTheme *theme,
}
for (i = 0; i < (int)META_FRAME_TYPE_LAST; i++)
if (theme->style_sets_by_type[i] == NULL)
if (i != (int)META_FRAME_TYPE_ATTACHED && theme->style_sets_by_type[i] == NULL)
{
g_set_error (error, META_THEME_ERROR, META_THEME_ERROR_FAILED,
_("No frame style set for window type \"%s\" in theme \"%s\", add a <window type=\"%s\" style_set=\"whatever\"/> element"),
@@ -5074,7 +5074,10 @@ theme_get_style (MetaTheme *theme,
style_set = theme->style_sets_by_type[type];
/* Right now the parser forces a style set for all types,
if (style_set == NULL && type == META_FRAME_TYPE_ATTACHED)
style_set = theme->style_sets_by_type[META_FRAME_TYPE_BORDER];
/* Right now the parser forces a style set for all other types,
* but this fallback code is here in case I take that out.
*/
if (style_set == NULL)
@@ -6004,6 +6007,8 @@ meta_frame_type_from_string (const char *str)
return META_FRAME_TYPE_MENU;
else if (strcmp ("border", str) == 0)
return META_FRAME_TYPE_BORDER;
else if (strcmp ("attached", str) == 0)
return META_FRAME_TYPE_ATTACHED;
#if 0
else if (strcmp ("toolbar", str) == 0)
return META_FRAME_TYPE_TOOLBAR;
@@ -6012,6 +6017,14 @@ meta_frame_type_from_string (const char *str)
return META_FRAME_TYPE_LAST;
}
/**
* meta_frame_type_to_string:
*
* Converts a frame type enum value to the name string that would
* appear in the theme definition file.
*
* Return value: the string value
*/
const char*
meta_frame_type_to_string (MetaFrameType type)
{
@@ -6029,6 +6042,8 @@ meta_frame_type_to_string (MetaFrameType type)
return "menu";
case META_FRAME_TYPE_BORDER:
return "border";
case META_FRAME_TYPE_ATTACHED:
return "attached";
#if 0
case META_FRAME_TYPE_TOOLBAR:
return "toolbar";
@@ -6074,16 +6089,20 @@ meta_gradient_type_to_string (MetaGradientType type)
GtkStateType
meta_gtk_state_from_string (const char *str)
{
if (strcmp ("normal", str) == 0 || strcmp ("NORMAL", str) == 0)
if (g_ascii_strcasecmp ("normal", str) == 0)
return GTK_STATE_NORMAL;
else if (strcmp ("prelight", str) == 0 || strcmp ("PRELIGHT", str) == 0)
else if (g_ascii_strcasecmp ("prelight", str) == 0)
return GTK_STATE_PRELIGHT;
else if (strcmp ("active", str) == 0 || strcmp ("ACTIVE", str) == 0)
else if (g_ascii_strcasecmp ("active", str) == 0)
return GTK_STATE_ACTIVE;
else if (strcmp ("selected", str) == 0 || strcmp ("SELECTED", str) == 0)
else if (g_ascii_strcasecmp ("selected", str) == 0)
return GTK_STATE_SELECTED;
else if (strcmp ("insensitive", str) == 0 || strcmp ("INSENSITIVE", str) == 0)
else if (g_ascii_strcasecmp ("insensitive", str) == 0)
return GTK_STATE_INSENSITIVE;
else if (g_ascii_strcasecmp ("inconsistent", str) == 0)
return GTK_STATE_INCONSISTENT;
else if (g_ascii_strcasecmp ("focused", str) == 0)
return GTK_STATE_FOCUSED;
else
return -1; /* hack */
}
@@ -6103,6 +6122,10 @@ meta_gtk_state_to_string (GtkStateType state)
return "SELECTED";
case GTK_STATE_INSENSITIVE:
return "INSENSITIVE";
case GTK_STATE_INCONSISTENT:
return "INCONSISTENT";
case GTK_STATE_FOCUSED:
return "FOCUSED";
}
return "<unknown>";

View File

@@ -195,7 +195,7 @@ meta_tile_preview_show (MetaTilePreview *preview,
gtk_widget_show (preview->preview_window);
window = gtk_widget_get_window (preview->preview_window);
meta_core_lower_beneath_focus_window (GDK_DISPLAY_XDISPLAY (gdk_display_get_default ()),
GDK_WINDOW_XWINDOW (window),
GDK_WINDOW_XID (window),
gtk_get_current_event_time ());
old_rect.x = old_rect.y = 0;
@@ -254,5 +254,5 @@ meta_tile_preview_get_xwindow (MetaTilePreview *preview,
if (create_serial)
*create_serial = preview->create_serial;
return GDK_WINDOW_XWINDOW (window);
return GDK_WINDOW_XID (window);
}

View File

@@ -60,6 +60,16 @@ struct _MetaUI
void
meta_ui_init (int *argc, char ***argv)
{
/* As of 2.91.7, Gdk uses XI2 by default, which conflicts with the
* direct X calls we use - in particular, events caused by calls to
* XGrabPointer/XGrabKeyboard are no longer understood by GDK, while
* GDK will no longer generate the core XEvents we process.
* So at least for now, enforce the previous behavior.
*/
#if GTK_CHECK_VERSION(2, 91, 7)
gdk_disable_multidevice ();
#endif
if (!gtk_init_check (argc, argv))
meta_fatal ("Unable to open X display %s\n", XDisplayName (NULL));
@@ -91,6 +101,7 @@ static gboolean
maybe_redirect_mouse_event (XEvent *xevent)
{
GdkDisplay *gdisplay;
GdkDeviceManager *gmanager;
MetaUI *ui;
GdkEvent *gevent;
GdkWindow *gdk_window;
@@ -118,7 +129,7 @@ maybe_redirect_mouse_event (XEvent *xevent)
if (!ui)
return FALSE;
gdk_window = gdk_window_lookup_for_display (gdisplay, window);
gdk_window = gdk_x11_window_lookup_for_display (gdisplay, window);
if (gdk_window == NULL)
return FALSE;
@@ -196,7 +207,8 @@ maybe_redirect_mouse_event (XEvent *xevent)
}
/* If we've gotten here, we've created the gdk_event and should send it on */
gdk_event_set_device (gevent, gdk_display_get_core_pointer (gdisplay));
gmanager = gdk_display_get_device_manager (gdisplay);
gdk_event_set_device (gevent, gdk_device_manager_get_client_pointer (gmanager));
gtk_main_do_event (gevent);
gdk_event_free (gevent);
@@ -399,8 +411,10 @@ meta_ui_map_frame (MetaUI *ui,
Window xwindow)
{
GdkWindow *window;
GdkDisplay *display;
window = gdk_xid_table_lookup (xwindow);
display = gdk_x11_lookup_xdisplay (ui->xdisplay);
window = gdk_x11_window_lookup_for_display (display, xwindow);
if (window)
gdk_window_show_unraised (window);
@@ -411,8 +425,10 @@ meta_ui_unmap_frame (MetaUI *ui,
Window xwindow)
{
GdkWindow *window;
GdkDisplay *display;
window = gdk_xid_table_lookup (xwindow);
display = gdk_x11_lookup_xdisplay (ui->xdisplay);
window = gdk_x11_window_lookup_for_display (display, xwindow);
if (window)
gdk_window_hide (window);
@@ -641,8 +657,10 @@ meta_ui_window_should_not_cause_focus (Display *xdisplay,
Window xwindow)
{
GdkWindow *window;
GdkDisplay *display;
window = gdk_xid_table_lookup (xwindow);
display = gdk_x11_lookup_xdisplay (xdisplay);
window = gdk_x11_window_lookup_for_display (display, xwindow);
/* we shouldn't cause focus if we're an override redirect
* toplevel which is not foreign
@@ -657,17 +675,20 @@ char*
meta_text_property_to_utf8 (Display *xdisplay,
const XTextProperty *prop)
{
GdkDisplay *display;
char **list;
int count;
char *retval;
list = NULL;
count = gdk_text_property_to_utf8_list (gdk_x11_xatom_to_atom (prop->encoding),
prop->format,
prop->value,
prop->nitems,
&list);
display = gdk_x11_lookup_xdisplay (xdisplay);
count = gdk_text_property_to_utf8_list_for_display (display,
gdk_x11_xatom_to_atom (prop->encoding),
prop->format,
prop->value,
prop->nitems,
&list);
if (count == 0)
retval = NULL;
@@ -892,9 +913,11 @@ gboolean
meta_ui_window_is_widget (MetaUI *ui,
Window xwindow)
{
GdkDisplay *display;
GdkWindow *window;
window = gdk_xid_table_lookup (xwindow);
display = gdk_x11_lookup_xdisplay (ui->xdisplay);
window = gdk_x11_window_lookup_for_display (display, xwindow);
if (window)
{