Compare commits

..

47 Commits

Author SHA1 Message Date
Jasper St. Pierre
0829749049 wayland: Implement support for wl_viewport 2014-11-05 10:14:35 -08:00
Jasper St. Pierre
725d0ad680 shaped-texture: Add API to support a viewport view
This will be used by our Wayland front-end to implement the wl_viewport
extension.
2014-11-05 10:14:35 -08:00
Jonathon Jongsma
e72c6916aa xrandr: ignore hotplug_mode_update value
The important thing is whether this property exists or not, but the value
doesn't matter.
2014-11-05 11:58:34 -06:00
Emmanuele Bassi
8b98cb818c config: Fix compilation errors 2014-11-05 12:30:05 +00:00
Jasper St. Pierre
f8a4d450a5 Makefile: Use $(NULL) trick
This helps make the Makefile look nicer and prevent merge conflicts.
2014-11-04 19:08:07 -08:00
Jasper St. Pierre
681cf95236 monitor-config: Factor out some code to make a default config 2014-11-03 14:40:12 -08:00
Jasper St. Pierre
90d6734f8c cursor-renderer-x11: Include Xfixes headers as well 2014-11-03 10:51:40 -08:00
Jasper St. Pierre
213cd8a334 cursor-tracker: Make sure to include Xfixes headers
We use Xfixes symbols in this file, but under some conditions, we don't
always include the Xfixes headers.
2014-11-03 10:32:13 -08:00
Rui Matos
3b1271d9be monitor-config: Prevent a crash applying config for a closed lid
When a laptop's lid is closed we try to build and apply a temporary
configuration that disables the laptop's display if we have other
outputs.

This isn't enough though, we must also check if at least one of these
other outputs is enabled otherwise we'll try to resize the screen to
0x0 which (rightfully) hits an assertion.

https://bugzilla.gnome.org/show_bug.cgi?id=739450
2014-10-31 17:39:43 +01:00
Bastien Nocera
13b6bd20ca wayland: Don't check for hi-dpi on monitors with broken EDID
If the monitor reports a width/height that looks suspiciously like an
aspect ratio (16/9 or 16/10) don't check for hi-dpi. We can assume that
makers of devices that do support hi-dpi aren't so careless.

See http://cgit.freedesktop.org/~daniels/xserver/commit/?h=lodpi

https://bugzilla.gnome.org/show_bug.cgi?id=734839
2014-10-30 17:37:24 +01:00
Florian Müllner
63e31af476 Bump version to 3.15.1
Update NEWS.
2014-10-30 10:40:17 +00:00
Adel Gadllah
718a89eb2f meta-wayland-surface: Correcly scale the input region
The input region currently only gets scaled by the surface
scale while ignoring the output scale, which causes input events to not get
delivered correctly for clients on hidpi screens. So take the output scale
into account when doing so.

https://bugzilla.gnome.org/show_bug.cgi?id=739161
2014-10-27 18:11:53 +01:00
Adel Gadllah
a43ca7b5b1 Revert "wayland-surface: Apply the surface scale only if needed"
This commit is wrong, it assumes that the scale only applies to the one
set by the client but its not. meta_surface_actor_wayland_scale_texture
also handles the output scale. Revert the commit to fix hidpi for wayland
clients like weston-terminal.

This reverts commit 0364ea9140.

https://bugzilla.gnome.org/show_bug.cgi?id=739161
2014-10-27 18:11:53 +01:00
Florian Müllner
af00ca534a ui: Adapt to GtkStyleContext changes
Since GTK+ commit 3a337156d11a86c7, save()/restore() may only be
used for subelements; in this particular case, the change broke
the backdrop state in decorations. Luckily we don't actually need
the save()/restore() pair anyway, as we only touch the context's
state and always set it explicitly.
2014-10-25 18:16:49 +02:00
Jasper St. Pierre
4b2b431700 config: Combine two exit paths 2014-10-23 16:04:43 -07:00
Jasper St. Pierre
2c1a6b6a12 config: Fix an incorrect unref 2014-10-23 16:04:43 -07:00
Jasper St. Pierre
b3544f8ec1 window: Placate new gcc
It thinks remaining can be used uninitialized. It's wrong, but let's
help it out by initializing the variable.
2014-10-23 16:04:42 -07:00
Carlos Garnacho
df384965c3 core: Unset "pointer emulating" sequence after event processing
The set/unset branches of meta_display_update_pointer_emulating_sequence()
have been split and put directly where it makes sense. The pointer emulated
sequence will be updated before processing the CLUTTER_TOUCH_BEGIN, and
after processing the CLUTTER_TOUCH_END, this way the checks on this hold
true during all the sequence lifetime.

https://bugzilla.gnome.org/show_bug.cgi?id=738411
2014-10-23 17:16:59 +02:00
Carlos Garnacho
700d367937 compositor: Ensure child actors are included in picking
If the actor surface has an input mask, custom picking is implemented
for the portions affected by the mask, although the child actors (most
usually subsurfaces) are left out.

https://bugzilla.gnome.org/show_bug.cgi?id=738890
2014-10-23 16:53:44 +02:00
Carlos Garnacho
7c5989c978 wayland: Avoid MetaWindow call on non window-backed surfaces
Crossing events may also be gotten on subsurfaces.

https://bugzilla.gnome.org/show_bug.cgi?id=738890
2014-10-23 16:53:44 +02:00
Carlos Garnacho
8819d9ce66 core: end-of-grab button releases must be consumed by the window
Returning FALSE here gets the button release event propagated to the
client on wayland, which is unexpected after xdg_surface.move/resize()
have been called.

https://bugzilla.gnome.org/show_bug.cgi?id=738888
2014-10-23 16:53:44 +02:00
Rui Matos
b63413e5b0 Revert "monitor-manager: Remove needless code"
It turns out that this was wrong because MetaWindow->monitor points to
the old monitor infos and they are needed to position windows in the
new configuration which happens in a monitors-changed handler.

This reverts commit e1704acda4.
2014-10-23 16:38:26 +02:00
Jasper St. Pierre
cd1e1d4bf1 config: Fix a few memory leaks
We were forgetting to unref in a few places.
2014-10-22 15:51:22 -07:00
Jasper St. Pierre
9710c013c5 config: Rename ret to config
We don't return this configuration, so don't name it ret.
2014-10-22 15:49:47 -07:00
Jasper St. Pierre
05f8d79323 config: Fix the memory and other management for MetaMonitorConfig
The code in MetaMonitorConfig was really complex and was trying to do
way too much, using multiple different variables to determine where
things were stored, and trying to do fancy tricks to transfer
ownership.

Add a refcounting system to help simplify this, and clean up the logic.
Simply along the way, this fixes multiple bugs in the monitor config
logic, most notably bug #734889, which was my original goal to fix.
2014-10-22 15:25:12 -07:00
Jasper St. Pierre
f2546dfeea config: Refactor the check for the lid special-case out
This also fixes the case where current_is_for_laptop_lid wasn't properly
set in the default case.
2014-10-22 15:04:34 -07:00
Rui Matos
47e339b46e monitor-manager-xrandr: Don't do extra work on RRScreenChangeNotify
The X server sends several RRScreenChangeNotify events in a burst when
something happens which, currently, causes us to rebuild our view of
the world as many times and notify the upper layers about it which
causes a lot of bogus repeated work like rebuilding background actors.

We can avoid this extra work by looking at the timestamp in the
XRRScreenResources struct which is updated when an X client (including
us!) last changed something and comparing it with the previous
timestamp.

https://bugzilla.gnome.org/show_bug.cgi?id=738630
2014-10-16 20:39:16 +02:00
Rui Matos
016b8f5b4a monitor-manager-xrandr: Use CurrentTime when applying configurations
This is what the xrandr CLI tool does and will allow us to do less
work when we get RRScreenChangeNotify events.

https://bugzilla.gnome.org/show_bug.cgi?id=738630
2014-10-16 20:39:16 +02:00
Rui Matos
b3821c4f90 monitor-manager: Don't try to match the outputs on hotplug
meta_monitor_config_match_current() only matches the number of outputs
and if the output connector, vendor, product and serial match.

In the X backend, this means that we can't use it to bypass doing any
work because it won't detect cases where we actually want to update
ourselves like e.g. an output being turned off either by us or by
another X client (e.g. xrandr).

In the native backend, unlike the xrandr backend, we only get called
on real hotplug events and thus should always trigger the common
hotplug code to (possibly) apply a new mode so the check is pointless
anyway.

https://bugzilla.gnome.org/show_bug.cgi?id=738630
2014-10-16 20:39:16 +02:00
Rui Matos
29e5c6c363 monitor-manager-xrandr: Re-work xrandr event handling
In randr events, configTimestamp can be considered the hotplug time,
i.e. whenever the server notices hardware changes, this value will be
updated.

Having that in mind, we can re-work the logic to make it clearer.
There are no semantic changes.
2014-10-16 20:39:16 +02:00
Rui Matos
e1704acda4 monitor-manager: Remove needless code
Nothing uses this. Signal handlers have access to the new monitor
infos built by make_logical_config() .

https://bugzilla.gnome.org/show_bug.cgi?id=738630
2014-10-16 20:39:16 +02:00
Jasper St. Pierre
60cbb41f42 configure: Actually make gbm optional
Whoops, I made the code work without it, but forgot to strip it from the
actual list of requires packages.

Spotted-by: Rico Tzschichholz <ricotz@ubuntu.com>
2014-10-15 11:13:48 -07:00
Jasper St. Pierre
d88c8d9ced Make gbm optional
Now it's only required by the native backend. The cursor code is getting
quite messy, but it was already considerable messy to start with.
2014-10-15 10:40:28 -07:00
Jasper St. Pierre
60ab11ecbf cursor: Clean up code flow slightly
Reverse the set of expressions so testing for gbm is at the top.
2014-10-15 10:36:42 -07:00
Jasper St. Pierre
34516aeab6 display: Fix accidental inversion from 2f9c601
Commit 2f9c601 accidentally changed the logic here, changing the grab
behavior when not using raise-on-click. Fix this.

Spotted-by: Adam Goode <adam@spicenitz.org>
2014-10-14 22:20:33 -07:00
Cosimo Cecchi
a37f632b1b background: use GFiles instead of filenames
We want to use GResources for system backgrounds, so move this to a
GFile.

https://bugzilla.gnome.org/show_bug.cgi?id=736936
2014-10-14 18:54:36 -07:00
Jasper St. Pierre
8a6542c242 theme: Remove COLORIZE feature of images
From a quick code search and grep of gnome-themes-standard, none of
the themes that I inspected used this feature. Since it's the last
thing that uses a lot of old legacy GdkPixbuf code, I'd rather just
consider the feature unsupported at this point and clean up everything
I need to.

https://bugzilla.gnome.org/show_bug.cgi?id=662962
2014-10-14 17:45:14 -07:00
Jasper St. Pierre
7e12000d97 theme: Use cairo for drawing uncolorized IMAGEs
https://bugzilla.gnome.org/show_bug.cgi?id=662962
2014-10-14 17:45:14 -07:00
Jasper St. Pierre
23f086da8a theme: Use cairo for TINT operations with alpha
https://bugzilla.gnome.org/show_bug.cgi?id=662962
2014-10-14 17:45:14 -07:00
Jasper St. Pierre
4c2c1c4dd2 theme: Use cairo for drawing ICON
Thanks to Benjamin Otte for helping me clean this up.

https://bugzilla.gnome.org/show_bug.cgi?id=662962
2014-10-14 17:45:07 -07:00
Jasper St. Pierre
545f298921 theme: Remove our own gradient stuff
Part one of porting to cairo.

Thanks to Benjamin Otte for helping me clean this up.

https://bugzilla.gnome.org/show_bug.cgi?id=662962
2014-10-14 17:37:49 -07:00
Jasper St. Pierre
64295e8cd7 keybindings: Always freeze on the stage window
This is what gnome-shell does, so there's no reason to include this as
part of our API.
2014-10-14 14:36:47 -07:00
Jasper St. Pierre
722d4c6c17 native: Implement monitor hotplug in the native backend
Use gudev to notice when new monitors are hotplugged, and when they are,
update the configuration.
2014-10-14 13:56:48 -07:00
Jasper St. Pierre
cc8462969d monitor-manager: Put the common hotplug code in a common path as well
So we can reuse it in the KMS backend.
2014-10-14 13:56:48 -07:00
Jasper St. Pierre
989bb6ebb1 monitor-manager: Simplify reading the current configuration
Make a wrapper in MetaMonitorManager that handles freeing the existing
configuration for us.
2014-10-14 13:56:48 -07:00
Jasper St. Pierre
1dbda68839 monitor-manager-xrandr: Refactor handle_xevent once more
Make the flow a bit clearer, and inline a method only used once.
2014-10-14 13:56:48 -07:00
Florian Müllner
a460f88b31 Remove unused variable 2014-10-14 21:49:47 +02:00
38 changed files with 1864 additions and 2904 deletions

2
.gitignore vendored
View File

@@ -66,6 +66,8 @@ src/meta-dbus-idle-monitor.[ch]
src/meta-dbus-login1.[ch]
src/gtk-shell-protocol.c
src/gtk-shell-server-protocol.h
src/scaler-protocol.c
src/scaler-server-protocol.h
src/xdg-shell-protocol.c
src/xdg-shell-server-protocol.h
src/xserver-protocol.c

20
NEWS
View File

@@ -1,20 +1,14 @@
3.14.2
3.15.1
======
* Prevent crash applying monitor config for a closed lid [Rui; #739450]
* Misc. fixes [Rui, Jonathon, Jasper; #738630]
Contributors:
Jonathon Jongsma, Rui Matos, Jasper St. Pierre
3.14.1.5
========
* Use GResources for theme loading [Cosimo; #736936]
* Fix headerbar drag getting stuck on xwayland [Carlos; #738411]
* Fix wayland hiDPI regressions [Adel; #739161]
* Misc bug fixes and cleanups [Jasper, Rui, Carlos; #662962, #738630, #738888,
#738890]
Contributors:
Adel Gadllah, Florian Müllner, Jasper St. Pierre
Translations:
Dušan Kazik [sk]
Cosimo Cecchi, Adel Gadllah, Carlos Garnacho, Rui Matos, Florian Müllner,
Jasper St. Pierre
3.14.1
======

View File

@@ -1,8 +1,8 @@
AC_PREREQ(2.62)
m4_define([mutter_major_version], [3])
m4_define([mutter_minor_version], [14])
m4_define([mutter_micro_version], [2])
m4_define([mutter_minor_version], [15])
m4_define([mutter_micro_version], [1])
m4_define([mutter_version],
[mutter_major_version.mutter_minor_version.mutter_micro_version])
@@ -200,7 +200,7 @@ AC_SUBST(XWAYLAND_PATH)
PKG_CHECK_MODULES(MUTTER, $MUTTER_PC_MODULES)
PKG_CHECK_MODULES(MUTTER_NATIVE_BACKEND, [clutter-egl-1.0 libdrm libsystemd libinput gbm >= 10.3], [have_native_backend=yes], [have_native_backend=no])
PKG_CHECK_MODULES(MUTTER_NATIVE_BACKEND, [clutter-egl-1.0 libdrm libsystemd libinput gudev-1.0 gbm >= 10.3], [have_native_backend=yes], [have_native_backend=no])
if test $have_native_backend = yes; then
AC_DEFINE([HAVE_NATIVE_BACKEND],[1],[Define if you want to enable the native (KMS) backend based on systemd])
fi

View File

@@ -259,8 +259,6 @@ Overview of Theme Format Version 1
<!-- color obtained by a 0.5 alpha composite of the second color onto the first -->
<color value="blend/gtk:bg[SELECTED]/gtk:fg[SELECTED]/0.5"/>
</gradient>
<!-- image has an optional colorize="#color" attribute to give the
image a certain color -->
<image filename="foo.png" alpha="0.7"
x="10" y="30" width="width / 3" height="height / 4"/>
<gtk_arrow state="normal" shadow="in" arrow="up"

1443
po/sk.po

File diff suppressed because it is too large Load Diff

View File

@@ -41,11 +41,9 @@ endif
# Some random test programs for bits of the code
testboxes_SOURCES = core/testboxes.c
testgradient_SOURCES = ui/testgradient.c
testasyncgetprop_SOURCES = x11/testasyncgetprop.c
noinst_PROGRAMS+=testboxes testgradient testasyncgetprop
noinst_PROGRAMS+=testboxes testasyncgetprop
testboxes_LDADD = $(MUTTER_LIBS) libmutter.la
testgradient_LDADD = $(MUTTER_LIBS) libmutter.la
testasyncgetprop_LDADD = $(MUTTER_LIBS) libmutter.la

View File

@@ -6,6 +6,7 @@ lib_LTLIBRARIES = libmutter.la
SUBDIRS=compositor/plugins
EXTRA_DIST =
NULL =
AM_CPPFLAGS = \
-DCLUTTER_ENABLE_COMPOSITOR_API \
@@ -31,26 +32,32 @@ AM_CPPFLAGS = \
-DMUTTER_PKGLIBDIR=\"$(pkglibdir)\" \
-DMUTTER_PLUGIN_DIR=\"$(MUTTER_PLUGIN_DIR)\" \
-DGETTEXT_PACKAGE=\"$(GETTEXT_PACKAGE)\" \
-DXWAYLAND_PATH=\"$(XWAYLAND_PATH)\"
-DXWAYLAND_PATH=\"$(XWAYLAND_PATH)\" \
$(NULL)
mutter_built_sources = \
$(dbus_idle_built_sources) \
$(dbus_display_config_built_sources) \
$(dbus_login1_built_sources) \
mutter-enum-types.h \
mutter-enum-types.c
mutter-enum-types.c \
$(NULL)
if HAVE_WAYLAND
mutter_built_sources += \
gtk-shell-protocol.c \
gtk-shell-server-protocol.h \
xdg-shell-protocol.c \
xdg-shell-server-protocol.h
xdg-shell-server-protocol.h \
scaler-protocol.c \
scaler-server-protocol.h \
$(NULL)
endif
wayland_protocols = \
wayland_protocols = \
wayland/protocol/gtk-shell.xml \
wayland/protocol/xdg-shell.xml
wayland/protocol/xdg-shell.xml \
$(NULL)
libmutter_la_SOURCES = \
backends/meta-backend.c \
@@ -162,8 +169,6 @@ libmutter_la_SOURCES = \
meta/errors.h \
core/frame.c \
core/frame.h \
ui/gradient.c \
meta/gradient.h \
core/meta-gesture-tracker.c \
core/meta-gesture-tracker-private.h \
core/keybindings.c \
@@ -222,7 +227,8 @@ libmutter_la_SOURCES = \
x11/window-x11-private.h \
x11/xprops.c \
x11/xprops.h \
x11/mutter-Xatomtype.h
x11/mutter-Xatomtype.h \
$(NULL)
if HAVE_WAYLAND
libmutter_la_SOURCES += \
@@ -255,7 +261,8 @@ libmutter_la_SOURCES += \
wayland/meta-wayland-outputs.c \
wayland/meta-wayland-outputs.h \
wayland/window-wayland.c \
wayland/window-wayland.h
wayland/window-wayland.h \
$(NULL)
endif
if HAVE_NATIVE_BACKEND
@@ -271,11 +278,11 @@ libmutter_la_SOURCES += \
backends/native/meta-launcher.c \
backends/native/meta-launcher.h \
backends/native/dbus-utils.c \
backends/native/dbus-utils.h
backends/native/dbus-utils.h \
$(NULL)
endif
nodist_libmutter_la_SOURCES = \
$(mutter_built_sources)
nodist_libmutter_la_SOURCES = $(mutter_built_sources)
libmutter_la_LDFLAGS = -no-undefined
libmutter_la_LIBADD = $(MUTTER_LIBS) $(MUTTER_NATIVE_BACKEND_LIBS)
@@ -290,7 +297,6 @@ libmutterinclude_headers = \
meta/compositor.h \
meta/display.h \
meta/errors.h \
meta/gradient.h \
meta/group.h \
meta/keybindings.h \
meta/main.h \
@@ -311,7 +317,8 @@ libmutterinclude_headers = \
meta/types.h \
meta/util.h \
meta/window.h \
meta/workspace.h
meta/workspace.h \
$(NULL)
libmutterinclude_built_headers = \
meta/meta-version.h
@@ -392,14 +399,15 @@ DISTCLEANFILES = \
pkgconfigdir = $(libdir)/pkgconfig
pkgconfig_DATA = libmutter.pc
EXTRA_DIST += \
$(wayland_protocols) \
libmutter.pc.in \
mutter-enum-types.h.in \
mutter-enum-types.c.in \
org.freedesktop.login1.xml \
EXTRA_DIST += \
$(wayland_protocols) \
libmutter.pc.in \
mutter-enum-types.h.in \
mutter-enum-types.c.in \
org.freedesktop.login1.xml \
org.gnome.Mutter.DisplayConfig.xml \
org.gnome.Mutter.IdleMonitor.xml
org.gnome.Mutter.IdleMonitor.xml \
$(NULL)
BUILT_SOURCES = \
$(mutter_built_sources) \

View File

@@ -27,7 +27,9 @@
* pointer abstraction"
*/
#include <config.h>
#include "config.h"
#include "meta-cursor-tracker-private.h"
#include <string.h>
#include <meta/main.h>
#include <meta/util.h>
@@ -38,11 +40,10 @@
#include <gdk/gdk.h>
#include <gdk/gdkx.h>
#include <X11/extensions/Xfixes.h>
#include "meta-backend-private.h"
#include "meta-cursor-private.h"
#include "meta-cursor-tracker-private.h"
G_DEFINE_TYPE (MetaCursorTracker, meta_cursor_tracker, G_TYPE_OBJECT);

View File

@@ -67,6 +67,7 @@ typedef struct {
} MetaOutputConfig;
typedef struct {
guint refcount;
MetaOutputKey *keys;
MetaOutputConfig *outputs;
unsigned int n_outputs;
@@ -77,7 +78,6 @@ struct _MetaMonitorConfig {
GHashTable *configs;
MetaConfiguration *current;
gboolean current_is_stored;
gboolean current_is_for_laptop_lid;
MetaConfiguration *previous;
@@ -124,11 +124,29 @@ config_clear (MetaConfiguration *config)
g_free (config->outputs);
}
static void
config_free (gpointer config)
static MetaConfiguration *
config_ref (MetaConfiguration *config)
{
config_clear (config);
g_slice_free (MetaConfiguration, config);
config->refcount++;
return config;
}
static void
config_unref (MetaConfiguration *config)
{
if (--config->refcount == 0)
{
config_clear (config);
g_slice_free (MetaConfiguration, config);
}
}
static MetaConfiguration *
config_new (void)
{
MetaConfiguration *config = g_slice_new0 (MetaConfiguration);
config->refcount = 1;
return config;
}
static unsigned long
@@ -220,7 +238,7 @@ meta_monitor_config_init (MetaMonitorConfig *self)
const char *filename;
char *path;
self->configs = g_hash_table_new_full (config_hash, config_equal, NULL, config_free);
self->configs = g_hash_table_new_full (config_hash, config_equal, NULL, (GDestroyNotify) config_unref);
filename = g_getenv ("MUTTER_MONITOR_FILENAME");
if (filename == NULL)
@@ -830,73 +848,45 @@ meta_monitor_config_get_stored (MetaMonitorConfig *self,
return stored;
}
static void
set_current (MetaMonitorConfig *self,
MetaConfiguration *config)
{
g_clear_pointer (&self->previous, (GDestroyNotify) config_unref);
self->previous = self->current;
self->current = config_ref (config);
}
static gboolean
apply_configuration (MetaMonitorConfig *self,
MetaConfiguration *config,
MetaMonitorManager *manager,
gboolean stored)
MetaMonitorManager *manager)
{
GPtrArray *crtcs, *outputs;
gboolean ret = FALSE;
crtcs = g_ptr_array_new_full (config->n_outputs, (GDestroyNotify)meta_crtc_info_free);
outputs = g_ptr_array_new_full (config->n_outputs, (GDestroyNotify)meta_output_info_free);
if (!meta_monitor_config_assign_crtcs (config, manager, crtcs, outputs))
{
g_ptr_array_unref (crtcs);
g_ptr_array_unref (outputs);
if (!stored)
config_free (config);
return FALSE;
}
goto out;
meta_monitor_manager_apply_configuration (manager,
(MetaCRTCInfo**)crtcs->pdata, crtcs->len,
(MetaOutputInfo**)outputs->pdata, outputs->len);
/* Stored (persistent) configurations override the previous one always.
Also, we clear the previous configuration if the current one (which is
about to become previous) is stored, or if the current one has
different outputs.
*/
if (stored ||
(self->current && self->current_is_stored))
{
if (self->previous)
config_free (self->previous);
self->previous = NULL;
}
else
{
/* Despite the name, config_equal() only checks the set of outputs,
not their modes
*/
if (self->current && config_equal (self->current, config))
{
self->previous = self->current;
}
else
{
if (self->current)
config_free (self->current);
self->previous = NULL;
}
}
set_current (self, config);
self->current = config;
self->current_is_stored = stored;
/* If true, we'll be overridden at the end of this call
inside turn_off_laptop_display()
*/
* inside turn_off_laptop_display / apply_configuration_with_lid */
self->current_is_for_laptop_lid = FALSE;
if (self->current == self->previous)
self->previous = NULL;
ret = TRUE;
out:
g_ptr_array_unref (crtcs);
g_ptr_array_unref (outputs);
return TRUE;
return ret;
}
static gboolean
@@ -948,7 +938,7 @@ make_laptop_lid_config (MetaConfiguration *reference)
g_assert (multiple_outputs_are_enabled (reference));
new = g_slice_new0 (MetaConfiguration);
new = config_new ();
new->n_outputs = reference->n_outputs;
new->keys = g_new0 (MetaOutputKey, reference->n_outputs);
new->outputs = g_new0 (MetaOutputConfig, reference->n_outputs);
@@ -1003,6 +993,32 @@ make_laptop_lid_config (MetaConfiguration *reference)
return new;
}
static gboolean
apply_configuration_with_lid (MetaMonitorConfig *self,
MetaConfiguration *config,
MetaMonitorManager *manager)
{
if (self->lid_is_closed &&
multiple_outputs_are_enabled (config) &&
laptop_display_is_on (config))
{
MetaConfiguration *laptop_lid_config = make_laptop_lid_config (config);
if (apply_configuration (self, laptop_lid_config, manager))
{
self->current_is_for_laptop_lid = TRUE;
config_unref (laptop_lid_config);
return TRUE;
}
else
{
config_unref (laptop_lid_config);
return FALSE;
}
}
else
return apply_configuration (self, config, manager);
}
gboolean
meta_monitor_config_apply_stored (MetaMonitorConfig *self,
MetaMonitorManager *manager)
@@ -1015,23 +1031,7 @@ meta_monitor_config_apply_stored (MetaMonitorConfig *self,
stored = meta_monitor_config_get_stored (self, outputs, n_outputs);
if (stored)
{
if (self->lid_is_closed &&
multiple_outputs_are_enabled (stored) &&
laptop_display_is_on (stored))
{
if (apply_configuration (self, make_laptop_lid_config (stored),
manager, FALSE))
{
self->current_is_for_laptop_lid = TRUE;
return TRUE;
}
else
return FALSE;
}
else
return apply_configuration (self, stored, manager, TRUE);
}
return apply_configuration_with_lid (self, stored, manager);
else
return FALSE;
}
@@ -1081,6 +1081,21 @@ find_primary_output (MetaOutput *outputs,
return best;
}
static void
init_config_from_preferred_mode (MetaOutputConfig *config,
MetaOutput *output)
{
config->enabled = TRUE;
config->rect.x = 0;
config->rect.y = 0;
config->rect.width = output->preferred_mode->width;
config->rect.height = output->preferred_mode->height;
config->refresh_rate = output->preferred_mode->refresh_rate;
config->transform = META_MONITOR_TRANSFORM_NORMAL;
config->is_primary = FALSE;
config->is_presentation = FALSE;
}
static MetaConfiguration *
make_default_config (MetaMonitorConfig *self,
MetaOutput *outputs,
@@ -1093,7 +1108,7 @@ make_default_config (MetaMonitorConfig *self,
MetaConfiguration *ret;
MetaOutput *primary;
ret = g_slice_new (MetaConfiguration);
ret = config_new ();
make_config_key (ret, outputs, n_outputs, -1);
ret->outputs = g_new0 (MetaOutputConfig, n_outputs);
@@ -1101,15 +1116,8 @@ make_default_config (MetaMonitorConfig *self,
nothing else to do */
if (n_outputs == 1)
{
ret->outputs[0].enabled = TRUE;
ret->outputs[0].rect.x = 0;
ret->outputs[0].rect.y = 0;
ret->outputs[0].rect.width = outputs[0].preferred_mode->width;
ret->outputs[0].rect.height = outputs[0].preferred_mode->height;
ret->outputs[0].refresh_rate = outputs[0].preferred_mode->refresh_rate;
ret->outputs[0].transform = META_MONITOR_TRANSFORM_NORMAL;
init_config_from_preferred_mode (&ret->outputs[0], &outputs[0]);
ret->outputs[0].is_primary = TRUE;
return ret;
}
@@ -1153,15 +1161,7 @@ make_default_config (MetaMonitorConfig *self,
}
else
{
ret->outputs[j].enabled = TRUE;
ret->outputs[j].rect.x = 0;
ret->outputs[j].rect.y = 0;
ret->outputs[j].rect.width = outputs[0].preferred_mode->width;
ret->outputs[j].rect.height = outputs[0].preferred_mode->height;
ret->outputs[j].refresh_rate = outputs[0].preferred_mode->refresh_rate;
ret->outputs[j].transform = META_MONITOR_TRANSFORM_NORMAL;
ret->outputs[j].is_primary = FALSE;
ret->outputs[j].is_presentation = FALSE;
init_config_from_preferred_mode (&ret->outputs[j], &outputs[0]);
}
}
@@ -1186,22 +1186,24 @@ make_default_config (MetaMonitorConfig *self,
x = primary->preferred_mode->width;
for (i = 0; i < n_outputs; i++)
{
MetaOutput *output = &outputs[i];
gboolean is_primary = (&outputs[i] == primary);
ret->outputs[i].enabled = TRUE;
ret->outputs[i].rect.x = (output == primary) ? 0 : x;
ret->outputs[i].rect.y = 0;
ret->outputs[i].rect.width = output->preferred_mode->width;
ret->outputs[i].rect.height = output->preferred_mode->height;
ret->outputs[i].refresh_rate = output->preferred_mode->refresh_rate;
ret->outputs[i].transform = META_MONITOR_TRANSFORM_NORMAL;
ret->outputs[i].is_primary = (output == primary);
init_config_from_preferred_mode (&ret->outputs[i], &outputs[i]);
ret->outputs[i].is_primary = is_primary;
if (is_primary)
{
ret->outputs[i].rect.x = 0;
}
else
{
ret->outputs[i].rect.x = x;
x += ret->outputs[i].rect.width;
}
/* Disable outputs that would go beyond framebuffer limits */
if (ret->outputs[i].rect.x + ret->outputs[i].rect.width > max_width)
ret->outputs[i].enabled = FALSE;
else if (output != primary)
x += output->preferred_mode->width;
}
return ret;
@@ -1213,7 +1215,7 @@ ensure_at_least_one_output (MetaMonitorConfig *self,
MetaOutput *outputs,
unsigned n_outputs)
{
MetaConfiguration *ret;
MetaConfiguration *config;
MetaOutput *primary;
unsigned i;
@@ -1224,34 +1226,29 @@ ensure_at_least_one_output (MetaMonitorConfig *self,
/* Oh no, we don't! Activate the primary one and disable everything else */
ret = g_slice_new (MetaConfiguration);
make_config_key (ret, outputs, n_outputs, -1);
ret->outputs = g_new0 (MetaOutputConfig, n_outputs);
config = config_new ();
make_config_key (config, outputs, n_outputs, -1);
config->outputs = g_new0 (MetaOutputConfig, n_outputs);
primary = find_primary_output (outputs, n_outputs);
for (i = 0; i < n_outputs; i++)
{
MetaOutput *output = &outputs[i];
gboolean is_primary = (&outputs[i] == primary);
if (output == primary)
if (is_primary)
{
ret->outputs[i].enabled = TRUE;
ret->outputs[i].rect.x = 0;
ret->outputs[i].rect.y = 0;
ret->outputs[i].rect.width = output->preferred_mode->width;
ret->outputs[i].rect.height = output->preferred_mode->height;
ret->outputs[i].refresh_rate = output->preferred_mode->refresh_rate;
ret->outputs[i].transform = META_MONITOR_TRANSFORM_NORMAL;
ret->outputs[i].is_primary = TRUE;
init_config_from_preferred_mode (&config->outputs[i], &outputs[0]);
config->outputs[i].is_primary = TRUE;
}
else
{
ret->outputs[i].enabled = FALSE;
config->outputs[i].enabled = FALSE;
}
}
apply_configuration (self, ret, manager, FALSE);
apply_configuration (self, config, manager);
config_unref (config);
return FALSE;
}
@@ -1262,7 +1259,7 @@ meta_monitor_config_make_default (MetaMonitorConfig *self,
MetaOutput *outputs;
MetaConfiguration *default_config;
unsigned n_outputs;
gboolean ok;
gboolean ok = FALSE;
int max_width, max_height;
outputs = meta_monitor_manager_get_outputs (manager, &n_outputs);
@@ -1275,22 +1272,11 @@ meta_monitor_config_make_default (MetaMonitorConfig *self,
}
default_config = make_default_config (self, outputs, n_outputs, max_width, max_height);
if (default_config != NULL)
{
if (self->lid_is_closed &&
multiple_outputs_are_enabled (default_config) &&
laptop_display_is_on (default_config))
{
ok = apply_configuration (self, make_laptop_lid_config (default_config),
manager, FALSE);
config_free (default_config);
}
else
ok = apply_configuration (self, default_config, manager, FALSE);
ok = apply_configuration_with_lid (self, default_config, manager);
config_unref (default_config);
}
else
ok = FALSE;
if (!ok)
{
@@ -1327,7 +1313,7 @@ meta_monitor_config_update_current (MetaMonitorConfig *self,
outputs = meta_monitor_manager_get_outputs (manager, &n_outputs);
current = g_slice_new (MetaConfiguration);
current = config_new ();
current->n_outputs = n_outputs;
current->outputs = g_new0 (MetaOutputConfig, n_outputs);
current->keys = g_new0 (MetaOutputKey, n_outputs);
@@ -1340,15 +1326,11 @@ meta_monitor_config_update_current (MetaMonitorConfig *self,
if (self->current && config_equal_full (current, self->current))
{
config_free (current);
config_unref (current);
return;
}
if (self->current && !self->current_is_stored)
config_free (self->current);
self->current = current;
self->current_is_stored = FALSE;
set_current (self, current);
}
void
@@ -1356,7 +1338,17 @@ meta_monitor_config_restore_previous (MetaMonitorConfig *self,
MetaMonitorManager *manager)
{
if (self->previous)
apply_configuration (self, self->previous, manager, FALSE);
{
/* The user chose to restore the previous configuration. In this
* case, restore the previous configuration. */
MetaConfiguration *prev_config = config_ref (self->previous);
apply_configuration (self, prev_config, manager);
config_unref (prev_config);
/* After this, self->previous contains the rejected configuration.
* Since it was rejected, nuke it. */
g_clear_pointer (&self->previous, (GDestroyNotify) config_unref);
}
else
{
if (!meta_monitor_config_apply_stored (self, manager))
@@ -1374,7 +1366,8 @@ turn_off_laptop_display (MetaMonitorConfig *self,
return;
new = make_laptop_lid_config (self->current);
apply_configuration (self, new, manager, FALSE);
apply_configuration (self, new, manager);
config_unref (new);
self->current_is_for_laptop_lid = TRUE;
}
@@ -1533,16 +1526,7 @@ meta_monitor_config_save (MetaMonitorConfig *self)
void
meta_monitor_config_make_persistent (MetaMonitorConfig *self)
{
if (self->current_is_stored)
return;
self->current_is_stored = TRUE;
g_hash_table_replace (self->configs, self->current, self->current);
if (self->previous)
config_free (self->previous);
self->previous = NULL;
g_hash_table_replace (self->configs, self->current, config_ref (self->current));
meta_monitor_config_save (self);
}

View File

@@ -58,14 +58,6 @@ meta_monitor_manager_init (MetaMonitorManager *manager)
{
}
static void
read_current_config (MetaMonitorManager *manager)
{
manager->serial++;
META_MONITOR_MANAGER_GET_CLASS (manager)->read_current (manager);
}
/*
* make_logical_config:
*
@@ -198,7 +190,7 @@ meta_monitor_manager_constructed (GObject *object)
manager->config = meta_monitor_config_new ();
read_current_config (manager);
meta_monitor_manager_read_current_config (manager);
if (!meta_monitor_config_apply_stored (manager->config, manager))
meta_monitor_config_make_default (manager->config, manager);
@@ -211,24 +203,7 @@ meta_monitor_manager_constructed (GObject *object)
so this is not needed.
*/
if (META_IS_MONITOR_MANAGER_XRANDR (manager))
{
MetaOutput *old_outputs;
MetaCRTC *old_crtcs;
MetaMonitorMode *old_modes;
unsigned int n_old_outputs, n_old_modes;
old_outputs = manager->outputs;
n_old_outputs = manager->n_outputs;
old_modes = manager->modes;
n_old_modes = manager->n_modes;
old_crtcs = manager->crtcs;
read_current_config (manager);
meta_monitor_manager_free_output_array (old_outputs, n_old_outputs);
meta_monitor_manager_free_mode_array (old_modes, n_old_modes);
g_free (old_crtcs);
}
meta_monitor_manager_read_current_config (manager);
make_logical_config (manager);
initialize_dbus_interface (manager);
@@ -236,7 +211,7 @@ meta_monitor_manager_constructed (GObject *object)
manager->in_init = FALSE;
}
void
static void
meta_monitor_manager_free_output_array (MetaOutput *old_outputs,
int n_old_outputs)
{
@@ -259,7 +234,7 @@ meta_monitor_manager_free_output_array (MetaOutput *old_outputs,
g_free (old_outputs);
}
void
static void
meta_monitor_manager_free_mode_array (MetaMonitorMode *old_modes,
int n_old_modes)
{
@@ -1162,6 +1137,31 @@ meta_monitor_manager_get_screen_limits (MetaMonitorManager *manager,
*height = manager->max_screen_height;
}
void
meta_monitor_manager_read_current_config (MetaMonitorManager *manager)
{
MetaOutput *old_outputs;
MetaCRTC *old_crtcs;
MetaMonitorMode *old_modes;
unsigned int n_old_outputs, n_old_modes;
/* Some implementations of read_current use the existing information
* we have available, so don't free the old configuration until after
* read_current finishes. */
old_outputs = manager->outputs;
n_old_outputs = manager->n_outputs;
old_modes = manager->modes;
n_old_modes = manager->n_modes;
old_crtcs = manager->crtcs;
manager->serial++;
META_MONITOR_MANAGER_GET_CLASS (manager)->read_current (manager);
meta_monitor_manager_free_output_array (old_outputs, n_old_outputs);
meta_monitor_manager_free_mode_array (old_modes, n_old_modes);
g_free (old_crtcs);
}
void
meta_monitor_manager_rebuild_derived (MetaMonitorManager *manager)
{
@@ -1179,3 +1179,22 @@ meta_monitor_manager_rebuild_derived (MetaMonitorManager *manager)
g_free (old_monitor_infos);
}
void
meta_monitor_manager_on_hotplug (MetaMonitorManager *manager)
{
gboolean applied_config = FALSE;
/* If the monitor has hotplug_mode_update (which is used by VMs), don't bother
* applying our stored configuration, because it's likely the user just resizing
* the window.
*/
if (!meta_monitor_manager_has_hotplug_mode_update (manager))
{
if (meta_monitor_config_apply_stored (manager->config, manager))
applied_config = TRUE;
}
/* If we haven't applied any configuration, apply the default configuration. */
if (!applied_config)
meta_monitor_config_make_default (manager->config, manager);
}

View File

@@ -339,11 +339,9 @@ void meta_monitor_manager_confirm_configuration (MetaMonitorManag
void meta_crtc_info_free (MetaCRTCInfo *info);
void meta_output_info_free (MetaOutputInfo *info);
void meta_monitor_manager_free_output_array (MetaOutput *old_outputs,
int n_old_outputs);
void meta_monitor_manager_free_mode_array (MetaMonitorMode *old_modes,
int n_old_modes);
gboolean meta_monitor_manager_has_hotplug_mode_update (MetaMonitorManager *manager);
void meta_monitor_manager_read_current_config (MetaMonitorManager *manager);
void meta_monitor_manager_on_hotplug (MetaMonitorManager *manager);
/* Returns true if transform causes width and height to be inverted
This is true for the odd transforms in the enum */

View File

@@ -24,6 +24,7 @@
#include "config.h"
#include "meta-monitor-manager-kms.h"
#include "meta-monitor-config.h"
#include <string.h>
#include <stdlib.h>
@@ -40,6 +41,8 @@
#include <meta/errors.h>
#include "edid.h"
#include <gudev/gudev.h>
typedef struct {
drmModeConnector *connector;
@@ -68,6 +71,8 @@ struct _MetaMonitorManagerKms
unsigned int n_encoders;
drmModeEncoder *current_encoder;
GUdevClient *udev;
};
struct _MetaMonitorManagerKmsClass
@@ -893,6 +898,23 @@ meta_monitor_manager_kms_set_crtc_gamma (MetaMonitorManager *manager,
drmModeCrtcSetGamma (manager_kms->fd, crtc->crtc_id, size, red, green, blue);
}
static void
on_uevent (GUdevClient *client,
const char *action,
GUdevDevice *device,
gpointer user_data)
{
MetaMonitorManagerKms *manager_kms = META_MONITOR_MANAGER_KMS (user_data);
MetaMonitorManager *manager = META_MONITOR_MANAGER (manager_kms);
if (!g_udev_device_get_property_as_boolean (device, "HOTPLUG"))
return;
meta_monitor_manager_read_current_config (manager);
meta_monitor_manager_on_hotplug (manager);
}
static void
meta_monitor_manager_kms_init (MetaMonitorManagerKms *manager_kms)
{
@@ -907,6 +929,21 @@ meta_monitor_manager_kms_init (MetaMonitorManagerKms *manager_kms)
cogl_renderer = cogl_display_get_renderer (cogl_display);
manager_kms->fd = cogl_kms_renderer_get_kms_fd (cogl_renderer);
const char *subsystems[2] = { "drm", NULL };
manager_kms->udev = g_udev_client_new (subsystems);
g_signal_connect (manager_kms->udev, "uevent",
G_CALLBACK (on_uevent), manager_kms);
}
static void
meta_monitor_manager_kms_dispose (GObject *object)
{
MetaMonitorManagerKms *manager_kms = META_MONITOR_MANAGER_KMS (object);
g_clear_object (&manager_kms->udev);
G_OBJECT_CLASS (meta_monitor_manager_kms_parent_class)->dispose (object);
}
static void
@@ -925,6 +962,7 @@ meta_monitor_manager_kms_class_init (MetaMonitorManagerKmsClass *klass)
MetaMonitorManagerClass *manager_class = META_MONITOR_MANAGER_CLASS (klass);
GObjectClass *object_class = G_OBJECT_CLASS (klass);
object_class->dispose = meta_monitor_manager_kms_dispose;
object_class->finalize = meta_monitor_manager_kms_finalize;
manager_class->read_current = meta_monitor_manager_kms_read_current;

View File

@@ -26,6 +26,8 @@
#include "meta-cursor-renderer-x11.h"
#include <X11/extensions/Xfixes.h>
#include "meta-backend-x11.h"
#include "meta-stage.h"

View File

@@ -57,7 +57,6 @@ struct _MetaMonitorManagerXrandr
Display *xdisplay;
XRRScreenResources *resources;
int time;
int rr_event_base;
int rr_error_base;
};
@@ -437,7 +436,6 @@ meta_monitor_manager_xrandr_read_current (MetaMonitorManager *manager)
return;
manager_xrandr->resources = resources;
manager_xrandr->time = resources->configTimestamp;
manager->n_outputs = resources->noutput;
manager->n_crtcs = resources->ncrtc;
manager->n_modes = resources->nmode;
@@ -771,7 +769,7 @@ meta_monitor_manager_xrandr_apply_configuration (MetaMonitorManager *manager,
XRRSetCrtcConfig (manager_xrandr->xdisplay,
manager_xrandr->resources,
(XID)crtc->crtc_id,
manager_xrandr->time,
CurrentTime,
0, 0,
None,
RR_Rotate_0,
@@ -801,7 +799,7 @@ meta_monitor_manager_xrandr_apply_configuration (MetaMonitorManager *manager,
XRRSetCrtcConfig (manager_xrandr->xdisplay,
manager_xrandr->resources,
(XID)crtc->crtc_id,
manager_xrandr->time,
CurrentTime,
0, 0,
None,
RR_Rotate_0,
@@ -884,7 +882,7 @@ meta_monitor_manager_xrandr_apply_configuration (MetaMonitorManager *manager,
ok = XRRSetCrtcConfig (manager_xrandr->xdisplay,
manager_xrandr->resources,
(XID)crtc->crtc_id,
manager_xrandr->time,
CurrentTime,
crtc_info->x, crtc_info->y,
(XID)mode->mode_id,
meta_monitor_transform_to_xrandr (crtc_info->transform),
@@ -1025,16 +1023,6 @@ meta_monitor_manager_xrandr_set_crtc_gamma (MetaMonitorManager *manager,
XRRFreeGamma (gamma);
}
static void
meta_monitor_manager_xrandr_rebuild_derived (MetaMonitorManager *manager)
{
/* This will be a no-op if the change was from our side, as
we already called it in the DBus method handler */
meta_monitor_config_update_current (manager->config, manager);
meta_monitor_manager_rebuild_derived (manager);
}
static void
meta_monitor_manager_xrandr_init (MetaMonitorManagerXrandr *manager_xrandr)
{
@@ -1094,57 +1082,30 @@ meta_monitor_manager_xrandr_handle_xevent (MetaMonitorManagerXrandr *manager_xra
XEvent *event)
{
MetaMonitorManager *manager = META_MONITOR_MANAGER (manager_xrandr);
MetaOutput *old_outputs;
MetaCRTC *old_crtcs;
MetaMonitorMode *old_modes;
unsigned int n_old_outputs, n_old_modes;
gboolean new_config;
gboolean applied_config = FALSE;
gboolean hotplug;
Time old_timestamp;
if ((event->type - manager_xrandr->rr_event_base) != RRScreenChangeNotify)
return FALSE;
XRRUpdateConfiguration (event);
/* Save the old structures, so they stay valid during the update */
old_outputs = manager->outputs;
n_old_outputs = manager->n_outputs;
old_modes = manager->modes;
n_old_modes = manager->n_modes;
old_crtcs = manager->crtcs;
old_timestamp = manager_xrandr->resources->timestamp;
manager->serial++;
meta_monitor_manager_xrandr_read_current (manager);
meta_monitor_manager_read_current_config (manager);
new_config = manager_xrandr->resources->timestamp >= manager_xrandr->resources->configTimestamp;
/* If this is the X server telling us we set a new configuration,
* we can simply short-cut to rebuilding our logical configuration.
*/
if (new_config)
hotplug = manager_xrandr->resources->timestamp < manager_xrandr->resources->configTimestamp;
if (hotplug)
{
meta_monitor_manager_xrandr_rebuild_derived (manager);
goto out;
/* This is a hotplug event, so go ahead and build a new configuration. */
meta_monitor_manager_on_hotplug (manager);
}
/* If the monitor has hotplug_mode_update (which is used by VMs), don't bother
* applying our stored configuration, because it's likely the user just resizing
* the window.
*/
if (!meta_monitor_manager_has_hotplug_mode_update (manager))
else
{
if (meta_monitor_config_apply_stored (manager->config, manager))
applied_config = TRUE;
/* If something else changed -- tell the world about it. */
if (old_timestamp < manager_xrandr->resources->timestamp)
meta_monitor_manager_rebuild_derived (manager);
}
/* If we haven't applied any configuration, apply the default configuration. */
if (!applied_config)
meta_monitor_config_make_default (manager->config, manager);
out:
meta_monitor_manager_free_output_array (old_outputs, n_old_outputs);
meta_monitor_manager_free_mode_array (old_modes, n_old_modes);
g_free (old_crtcs);
return TRUE;
}

View File

@@ -53,7 +53,7 @@ struct _MetaBackgroundImageCacheClass
struct _MetaBackgroundImage
{
GObject parent_instance;
char *filename;
GFile *file;
MetaBackgroundImageCache *cache;
gboolean in_cache;
gboolean loaded;
@@ -70,7 +70,7 @@ G_DEFINE_TYPE (MetaBackgroundImageCache, meta_background_image_cache, G_TYPE_OBJ
static void
meta_background_image_cache_init (MetaBackgroundImageCache *cache)
{
cache->images = g_hash_table_new (g_str_hash, g_str_equal);
cache->images = g_hash_table_new (g_file_hash, (GEqualFunc) g_file_equal);
}
static void
@@ -124,9 +124,17 @@ load_file (GTask *task,
{
GError *error = NULL;
GdkPixbuf *pixbuf;
GFileInputStream *stream;
pixbuf = gdk_pixbuf_new_from_file (image->filename,
&error);
stream = g_file_read (image->file, NULL, &error);
if (stream == NULL)
{
g_task_return_error (task, error);
return;
}
pixbuf = gdk_pixbuf_new_from_stream (G_INPUT_STREAM (stream), NULL, &error);
g_object_unref (stream);
if (pixbuf == NULL)
{
@@ -156,9 +164,11 @@ file_loaded (GObject *source_object,
if (pixbuf == NULL)
{
char *uri = g_file_get_uri (image->file);
g_warning ("Failed to load background '%s': %s",
image->filename, error->message);
uri, error->message);
g_clear_error (&error);
g_free (uri);
goto out;
}
@@ -195,7 +205,7 @@ out:
/**
* meta_background_image_cache_load:
* @cache: a #MetaBackgroundImageCache
* @filename: filename to load
* @file: #GFile to load
*
* Loads an image to use as a background, or returns a reference to an
* image that is already in the process of loading or loaded. In either
@@ -209,23 +219,23 @@ out:
*/
MetaBackgroundImage *
meta_background_image_cache_load (MetaBackgroundImageCache *cache,
const char *filename)
GFile *file)
{
MetaBackgroundImage *image;
GTask *task;
g_return_val_if_fail (META_IS_BACKGROUND_IMAGE_CACHE (cache), NULL);
g_return_val_if_fail (filename != NULL, NULL);
g_return_val_if_fail (file != NULL, NULL);
image = g_hash_table_lookup (cache->images, filename);
image = g_hash_table_lookup (cache->images, file);
if (image != NULL)
return g_object_ref (image);
image = g_object_new (META_TYPE_BACKGROUND_IMAGE, NULL);
image->cache = cache;
image->in_cache = TRUE;
image->filename = g_strdup (filename);
g_hash_table_insert (cache->images, image->filename, image);
image->file = g_object_ref (file);
g_hash_table_insert (cache->images, image->file, image);
task = g_task_new (image, NULL, file_loaded, NULL);
@@ -238,25 +248,25 @@ meta_background_image_cache_load (MetaBackgroundImageCache *cache,
/**
* meta_background_image_cache_purge:
* @cache: a #MetaBackgroundImageCache
* @filename: filename to remove from the cache
* @file: file to remove from the cache
*
* Remove an entry from the cache; this would be used if monitoring
* showed that the file changed.
*/
void
meta_background_image_cache_purge (MetaBackgroundImageCache *cache,
const char *filename)
GFile *file)
{
MetaBackgroundImage *image;
g_return_if_fail (META_IS_BACKGROUND_IMAGE_CACHE (cache));
g_return_if_fail (filename != NULL);
g_return_if_fail (file != NULL);
image = g_hash_table_lookup (cache->images, filename);
image = g_hash_table_lookup (cache->images, file);
if (image == NULL)
return;
g_hash_table_remove (cache->images, image->filename);
g_hash_table_remove (cache->images, image->file);
image->in_cache = FALSE;
}
@@ -273,12 +283,12 @@ meta_background_image_finalize (GObject *object)
MetaBackgroundImage *image = META_BACKGROUND_IMAGE (object);
if (image->in_cache)
g_hash_table_remove (image->cache->images, image->filename);
g_hash_table_remove (image->cache->images, image->file);
if (image->texture)
cogl_object_unref (image->texture);
if (image->filename)
g_free (image->filename);
if (image->file)
g_object_unref (image->file);
G_OBJECT_CLASS (meta_background_image_parent_class)->finalize (object);
}

View File

@@ -50,9 +50,9 @@ struct _MetaBackgroundPrivate
ClutterColor color;
ClutterColor second_color;
char *filename1;
GFile *file1;
MetaBackgroundImage *background_image1;
char *filename2;
GFile *file2;
MetaBackgroundImage *background_image2;
CoglTexture *color_texture;
@@ -241,16 +241,28 @@ on_background_loaded (MetaBackgroundImage *image,
mark_changed (self);
}
static void
set_filename (MetaBackground *self,
char **filenamep,
MetaBackgroundImage **imagep,
const char *filename)
static gboolean
file_equal0 (GFile *file1,
GFile *file2)
{
if (g_strcmp0 (filename, *filenamep) != 0)
if (file1 == file2)
return TRUE;
if ((file1 == NULL) || (file2 == NULL))
return FALSE;
return g_file_equal (file1, file2);
}
static void
set_file (MetaBackground *self,
GFile **filep,
MetaBackgroundImage **imagep,
GFile *file)
{
if (!file_equal0 (*filep, file))
{
g_free (*filenamep);
*filenamep = g_strdup (filename);
g_clear_object (filep);
if (*imagep)
{
@@ -261,10 +273,12 @@ set_filename (MetaBackground *self,
*imagep = NULL;
}
if (filename)
if (file)
{
MetaBackgroundImageCache *cache = meta_background_image_cache_get_default ();
*imagep = meta_background_image_cache_load (cache, filename);
*filep = g_object_ref (file);
*imagep = meta_background_image_cache_load (cache, file);
g_signal_connect (*imagep, "loaded",
G_CALLBACK (on_background_loaded), self);
}
@@ -280,8 +294,8 @@ meta_background_dispose (GObject *object)
free_color_texture (self);
free_wallpaper_texture (self);
set_filename (self, &priv->filename1, &priv->background_image1, NULL);
set_filename (self, &priv->filename2, &priv->background_image2, NULL);
set_file (self, &priv->file1, &priv->background_image1, NULL);
set_file (self, &priv->file2, &priv->background_image2, NULL);
set_screen (self, NULL);
@@ -867,19 +881,19 @@ meta_background_set_gradient (MetaBackground *self,
}
void
meta_background_set_filename (MetaBackground *self,
const char *filename,
GDesktopBackgroundStyle style)
meta_background_set_file (MetaBackground *self,
GFile *file,
GDesktopBackgroundStyle style)
{
g_return_if_fail (META_IS_BACKGROUND (self));
meta_background_set_blend (self, filename, NULL, 0.0, style);
meta_background_set_blend (self, file, NULL, 0.0, style);
}
void
meta_background_set_blend (MetaBackground *self,
const char *filename1,
const char *filename2,
GFile *file1,
GFile *file2,
double blend_factor,
GDesktopBackgroundStyle style)
{
@@ -890,8 +904,8 @@ meta_background_set_blend (MetaBackground *self,
priv = self->priv;
set_filename (self, &priv->filename1, &priv->background_image1, filename1);
set_filename (self, &priv->filename2, &priv->background_image2, filename2);
set_file (self, &priv->file1, &priv->background_image1, file1);
set_file (self, &priv->file2, &priv->background_image2, file2);
priv->blend_factor = blend_factor;
priv->style = style;

View File

@@ -34,4 +34,9 @@ void meta_shaped_texture_set_texture (MetaShapedTexture *stex,
CoglTexture *texture);
gboolean meta_shaped_texture_is_obscured (MetaShapedTexture *self);
void meta_shaped_texture_set_viewport (MetaShapedTexture *stex,
cairo_rectangle_int_t *src_rect,
int dest_width,
int dest_height);
#endif

View File

@@ -85,7 +85,12 @@ struct _MetaShapedTexturePrivate
cairo_region_t *clip_region;
cairo_region_t *unobscured_region;
/* Viewport stuff */
cairo_rectangle_int_t viewport_src_rect;
guint viewport_dest_width, viewport_dest_height;
guint tex_width, tex_height;
guint dest_width, dest_height;
guint create_mipmaps : 1;
};
@@ -136,7 +141,7 @@ set_unobscured_region (MetaShapedTexture *self,
g_clear_pointer (&priv->unobscured_region, (GDestroyNotify) cairo_region_destroy);
if (unobscured_region)
{
cairo_rectangle_int_t bounds = { 0, 0, priv->tex_width, priv->tex_height };
cairo_rectangle_int_t bounds = { 0, 0, priv->dest_width, priv->dest_height };
priv->unobscured_region = cairo_region_copy (unobscured_region);
cairo_region_intersect_rectangle (priv->unobscured_region, &bounds);
}
@@ -212,24 +217,45 @@ get_unblended_pipeline (CoglContext *ctx)
return cogl_pipeline_copy (template);
}
static void
scale_coords_by_src (cairo_rectangle_int_t *coords,
int dest_width, int dest_height,
cairo_rectangle_int_t *src_rect)
{
if (src_rect->width == 0 && src_rect->height == 0)
return;
coords->x = ((coords->x / dest_width) * src_rect->width) + src_rect->x;
coords->y = ((coords->y / dest_height) * src_rect->height) + src_rect->y;
coords->width = ((coords->width / dest_width) * src_rect->width);
coords->height = ((coords->height / dest_height) * src_rect->height);
}
static void
paint_clipped_rectangle (CoglFramebuffer *fb,
CoglPipeline *pipeline,
cairo_rectangle_int_t *rect,
ClutterActorBox *alloc)
ClutterActorBox *alloc,
cairo_rectangle_int_t *src_rect)
{
cairo_rectangle_int_t pixel_coords;
float coords[8];
float x1, y1, x2, y2;
float dest_width = alloc->x2 - alloc->x1;
float dest_height = alloc->y2 - alloc->y1;
x1 = rect->x;
y1 = rect->y;
x2 = rect->x + rect->width;
y2 = rect->y + rect->height;
coords[0] = rect->x / (alloc->x2 - alloc->x1);
coords[1] = rect->y / (alloc->y2 - alloc->y1);
coords[2] = (rect->x + rect->width) / (alloc->x2 - alloc->x1);
coords[3] = (rect->y + rect->height) / (alloc->y2 - alloc->y1);
pixel_coords = *rect;
scale_coords_by_src (&pixel_coords, dest_width, dest_height, src_rect);
coords[0] = pixel_coords.x / dest_width;
coords[1] = pixel_coords.y / dest_height;
coords[2] = (pixel_coords.x + pixel_coords.width) / dest_width;
coords[3] = (pixel_coords.y + pixel_coords.height) / dest_height;
coords[4] = coords[0];
coords[5] = coords[1];
@@ -241,6 +267,32 @@ paint_clipped_rectangle (CoglFramebuffer *fb,
&coords[0], 8);
}
static void
update_size (MetaShapedTexture *stex)
{
MetaShapedTexturePrivate *priv = stex->priv;
guint dest_width, dest_height;
if (priv->viewport_dest_width > 0)
dest_width = priv->viewport_dest_width;
else
dest_width = priv->tex_width;
if (priv->viewport_dest_height > 0)
dest_height = priv->viewport_dest_height;
else
dest_height = priv->tex_height;
if (priv->dest_width != dest_width ||
priv->dest_height != dest_height)
{
priv->dest_width = dest_width;
priv->dest_height = dest_height;
clutter_actor_queue_relayout (CLUTTER_ACTOR (stex));
g_signal_emit (stex, signals[SIZE_CHANGED], 0);
}
}
static void
set_cogl_texture (MetaShapedTexture *stex,
CoglTexture *cogl_tex)
@@ -274,8 +326,7 @@ set_cogl_texture (MetaShapedTexture *stex,
{
priv->tex_width = width;
priv->tex_height = height;
clutter_actor_queue_relayout (CLUTTER_ACTOR (stex));
g_signal_emit (stex, signals[SIZE_CHANGED], 0);
update_size (stex);
}
/* NB: We don't queue a redraw of the actor here because we don't
@@ -292,7 +343,7 @@ meta_shaped_texture_paint (ClutterActor *actor)
{
MetaShapedTexture *stex = (MetaShapedTexture *) actor;
MetaShapedTexturePrivate *priv = stex->priv;
guint tex_width, tex_height;
guint dest_width, dest_height;
guchar opacity;
CoglContext *ctx;
CoglFramebuffer *fb;
@@ -329,13 +380,13 @@ meta_shaped_texture_paint (ClutterActor *actor)
if (paint_tex == NULL)
return;
tex_width = priv->tex_width;
tex_height = priv->tex_height;
dest_width = priv->dest_width;
dest_height = priv->dest_height;
if (tex_width == 0 || tex_height == 0) /* no contents yet */
if (dest_width == 0 || dest_height == 0) /* no contents yet */
return;
cairo_rectangle_int_t tex_rect = { 0, 0, tex_width, tex_height };
cairo_rectangle_int_t tex_rect = { 0, 0, dest_width, dest_height };
/* Use nearest-pixel interpolation if the texture is unscaled. This
* improves performance, especially with software rendering.
@@ -418,7 +469,7 @@ meta_shaped_texture_paint (ClutterActor *actor)
{
cairo_rectangle_int_t rect;
cairo_region_get_rectangle (region, i, &rect);
paint_clipped_rectangle (fb, opaque_pipeline, &rect, &alloc);
paint_clipped_rectangle (fb, opaque_pipeline, &rect, &alloc, &priv->viewport_src_rect);
}
cogl_object_unref (opaque_pipeline);
@@ -473,16 +524,13 @@ meta_shaped_texture_paint (ClutterActor *actor)
if (!gdk_rectangle_intersect (&tex_rect, &rect, &rect))
continue;
paint_clipped_rectangle (fb, blended_pipeline, &rect, &alloc);
paint_clipped_rectangle (fb, blended_pipeline, &rect, &alloc, &priv->viewport_src_rect);
}
}
else
{
/* 3) blended_region is NULL. Do a full paint. */
cogl_framebuffer_draw_rectangle (fb, blended_pipeline,
0, 0,
alloc.x2 - alloc.x1,
alloc.y2 - alloc.y1);
paint_clipped_rectangle (fb, blended_pipeline, &tex_rect, &alloc, &priv->viewport_src_rect);
}
cogl_object_unref (blended_pipeline);
@@ -505,10 +553,10 @@ meta_shaped_texture_get_preferred_width (ClutterActor *self,
priv = META_SHAPED_TEXTURE (self)->priv;
if (min_width_p)
*min_width_p = priv->tex_width;
*min_width_p = priv->dest_width;
if (natural_width_p)
*natural_width_p = priv->tex_width;
*natural_width_p = priv->dest_width;
}
static void
@@ -524,10 +572,10 @@ meta_shaped_texture_get_preferred_height (ClutterActor *self,
priv = META_SHAPED_TEXTURE (self)->priv;
if (min_height_p)
*min_height_p = priv->tex_height;
*min_height_p = priv->dest_height;
if (natural_height_p)
*natural_height_p = priv->tex_height;
*natural_height_p = priv->dest_height;
}
static cairo_region_t *
@@ -762,6 +810,22 @@ meta_shaped_texture_set_opaque_region (MetaShapedTexture *stex,
priv->opaque_region = NULL;
}
void
meta_shaped_texture_set_viewport (MetaShapedTexture *stex,
cairo_rectangle_int_t *src_rect,
int dest_width,
int dest_height)
{
MetaShapedTexturePrivate *priv = stex->priv;
priv->viewport_src_rect = *src_rect;
priv->viewport_dest_width = dest_width;
priv->viewport_dest_height = dest_height;
update_size (stex);
clutter_actor_queue_redraw (CLUTTER_ACTOR (stex));
}
/**
* meta_shaped_texture_get_image:
* @stex: A #MetaShapedTexture

View File

@@ -65,6 +65,8 @@ meta_surface_actor_pick (ClutterActor *actor,
CoglContext *ctx;
CoglFramebuffer *fb;
CoglColor cogl_color;
ClutterActorIter iter;
ClutterActor *child;
n_rects = cairo_region_num_rectangles (priv->input_region);
rectangles = g_alloca (sizeof (float) * 4 * n_rects);
@@ -91,6 +93,11 @@ meta_surface_actor_pick (ClutterActor *actor,
cogl_pipeline_set_color (pipeline, &cogl_color);
cogl_framebuffer_draw_rectangles (fb, pipeline, rectangles, n_rects);
cogl_object_unref (pipeline);
clutter_actor_iter_init (&iter, actor);
while (clutter_actor_iter_next (&iter, &child))
clutter_actor_paint (child);
}
}
@@ -228,6 +235,16 @@ meta_surface_actor_set_opaque_region (MetaSurfaceActor *self,
meta_shaped_texture_set_opaque_region (priv->texture, region);
}
void
meta_surface_actor_set_viewport (MetaSurfaceActor *self,
cairo_rectangle_int_t *src_rect,
int dest_width,
int dest_height)
{
MetaSurfaceActorPrivate *priv = self->priv;
meta_shaped_texture_set_viewport (priv->texture, src_rect, dest_width, dest_height);
}
static gboolean
is_frozen (MetaSurfaceActor *self)
{

View File

@@ -61,6 +61,11 @@ void meta_surface_actor_set_input_region (MetaSurfaceActor *self,
void meta_surface_actor_set_opaque_region (MetaSurfaceActor *self,
cairo_region_t *region);
void meta_surface_actor_set_viewport (MetaSurfaceActor *self,
cairo_rectangle_int_t *src_rect,
int dest_width,
int dest_height);
void meta_surface_actor_process_damage (MetaSurfaceActor *actor,
int x, int y, int width, int height);
void meta_surface_actor_pre_paint (MetaSurfaceActor *actor);

View File

@@ -151,23 +151,6 @@ sequence_is_pointer_emulated (MetaDisplay *display,
return FALSE;
}
static void
meta_display_update_pointer_emulating_sequence (MetaDisplay *display,
const ClutterEvent *event)
{
ClutterEventSequence *sequence;
sequence = clutter_event_get_event_sequence (event);
if (event->type == CLUTTER_TOUCH_BEGIN &&
!display->pointer_emulating_sequence &&
sequence_is_pointer_emulated (display, event))
display->pointer_emulating_sequence = sequence;
else if (event->type == CLUTTER_TOUCH_END &&
display->pointer_emulating_sequence == sequence)
display->pointer_emulating_sequence = NULL;
}
static gboolean
meta_display_handle_event (MetaDisplay *display,
const ClutterEvent *event)
@@ -176,8 +159,15 @@ meta_display_handle_event (MetaDisplay *display,
gboolean bypass_clutter = FALSE;
G_GNUC_UNUSED gboolean bypass_wayland = FALSE;
MetaGestureTracker *tracker;
ClutterEventSequence *sequence;
meta_display_update_pointer_emulating_sequence (display, event);
sequence = clutter_event_get_event_sequence (event);
/* Set the pointer emulating sequence on touch begin, if eligible */
if (event->type == CLUTTER_TOUCH_BEGIN &&
!display->pointer_emulating_sequence &&
sequence_is_pointer_emulated (display, event))
display->pointer_emulating_sequence = sequence;
#ifdef HAVE_WAYLAND
MetaWaylandCompositor *compositor = NULL;
@@ -314,6 +304,11 @@ meta_display_handle_event (MetaDisplay *display,
}
#endif
/* Unset the pointer emulating sequence after its end event is processed */
if (event->type == CLUTTER_TOUCH_END &&
display->pointer_emulating_sequence == sequence)
display->pointer_emulating_sequence = NULL;
display->current_time = CurrentTime;
return bypass_clutter;
}

View File

@@ -1660,13 +1660,14 @@ meta_window_ungrab_all_keys (MetaWindow *window, guint32 timestamp)
}
void
meta_display_freeze_keyboard (MetaDisplay *display, Window window, guint32 timestamp)
meta_display_freeze_keyboard (MetaDisplay *display, guint32 timestamp)
{
MetaBackend *backend = meta_get_backend ();
if (!META_IS_BACKEND_X11 (backend))
return;
Window window = meta_backend_x11_get_xwindow (META_BACKEND_X11 (backend));
grab_keyboard (window, timestamp, XIGrabModeSync);
}

View File

@@ -5851,7 +5851,7 @@ update_resize (MetaWindow *window,
int new_w, new_h;
int gravity;
MetaRectangle old;
double remaining;
double remaining = 0;
MetaMaximizeFlags new_unmaximize;
window->display->grab_latest_motion_x = x;
@@ -6110,10 +6110,8 @@ meta_window_handle_mouse_grab_op_event (MetaWindow *window,
case CLUTTER_BUTTON_RELEASE:
if (event->button.button == 1 ||
event->button.button == (unsigned int) meta_prefs_get_mouse_button_resize ())
{
end_grab_op (window, event);
return FALSE;
}
end_grab_op (window, event);
return TRUE;
case CLUTTER_TOUCH_BEGIN:

View File

@@ -173,7 +173,6 @@ void meta_display_unmanage_screen (MetaDisplay *display,
void meta_display_clear_mouse_mode (MetaDisplay *display);
void meta_display_freeze_keyboard (MetaDisplay *display,
Window window,
guint32 timestamp);
void meta_display_ungrab_keyboard (MetaDisplay *display,
guint32 timestamp);

View File

@@ -1,71 +0,0 @@
/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
/* Mutter gradient rendering */
/*
* Copyright (C) 2001 Havoc Pennington, 99% copied from wrlib in
* WindowMaker, Copyright (C) 1997-2000 Dan Pascu and Alfredo Kojima
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License as
* published by the Free Software Foundation; either version 2 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, see <http://www.gnu.org/licenses/>. */
#ifndef META_GRADIENT_H
#define META_GRADIENT_H
#include <gdk-pixbuf/gdk-pixbuf.h>
#include <gdk/gdk.h>
/**
* MetaGradientType:
* @META_GRADIENT_VERTICAL: Vertical gradient
* @META_GRADIENT_HORIZONTAL: Horizontal gradient
* @META_GRADIENT_DIAGONAL: Diagonal gradient
* @META_GRADIENT_LAST: Marks the end of the #MetaGradientType enumeration
*
*/
typedef enum
{
META_GRADIENT_VERTICAL,
META_GRADIENT_HORIZONTAL,
META_GRADIENT_DIAGONAL,
META_GRADIENT_LAST
} MetaGradientType;
GdkPixbuf* meta_gradient_create_simple (int width,
int height,
const GdkRGBA *from,
const GdkRGBA *to,
MetaGradientType style);
GdkPixbuf* meta_gradient_create_multi (int width,
int height,
const GdkRGBA *colors,
int n_colors,
MetaGradientType style);
GdkPixbuf* meta_gradient_create_interwoven (int width,
int height,
const GdkRGBA colors1[2],
int thickness1,
const GdkRGBA colors2[2],
int thickness2);
/* Generate an alpha gradient and multiply it with the existing alpha
* channel of the given pixbuf
*/
void meta_gradient_add_alpha (GdkPixbuf *pixbuf,
const guchar *alphas,
int n_alphas,
MetaGradientType type);
#endif

View File

@@ -69,8 +69,8 @@ MetaBackgroundImageCache *meta_background_image_cache_get_default (void);
GType meta_background_image_cache_get_type (void);
MetaBackgroundImage *meta_background_image_cache_load (MetaBackgroundImageCache *cache,
const char *filename);
GFile *file);
void meta_background_image_cache_purge (MetaBackgroundImageCache *cache,
const char *filename);
GFile *file);
#endif /* __META_BACKGROUND_IMAGE_H__ */

View File

@@ -67,12 +67,12 @@ void meta_background_set_gradient (MetaBackground *self,
GDesktopBackgroundShading shading_direction,
ClutterColor *color,
ClutterColor *second_color);
void meta_background_set_filename (MetaBackground *self,
const char *filename,
void meta_background_set_file (MetaBackground *self,
GFile *file,
GDesktopBackgroundStyle style);
void meta_background_set_blend (MetaBackground *self,
const char *filename1,
const char *filename2,
GFile *file1,
GFile *file2,
double blend_factor,
GDesktopBackgroundStyle style);

View File

@@ -1,902 +0,0 @@
/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
/*
* Copyright (C) 2001 Havoc Pennington, 99% copied from wrlib in
* WindowMaker, Copyright (C) 1997-2000 Dan Pascu and Alfredo Kojima
* Copyright (C) 2005 Elijah Newren
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License as
* published by the Free Software Foundation; either version 2 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, see <http://www.gnu.org/licenses/>. */
/**
* SECTION:gradient
* @title: Gradients
* @short_description: Metacity gradient rendering
*/
#include <meta/gradient.h>
#include <meta/util.h>
#include <string.h>
/* This is all Alfredo's and Dan's usual very nice WindowMaker code,
* slightly GTK-ized
*/
static GdkPixbuf* meta_gradient_create_horizontal (int width,
int height,
const GdkRGBA *from,
const GdkRGBA *to);
static GdkPixbuf* meta_gradient_create_vertical (int width,
int height,
const GdkRGBA *from,
const GdkRGBA *to);
static GdkPixbuf* meta_gradient_create_diagonal (int width,
int height,
const GdkRGBA *from,
const GdkRGBA *to);
static GdkPixbuf* meta_gradient_create_multi_horizontal (int width,
int height,
const GdkRGBA *colors,
int count);
static GdkPixbuf* meta_gradient_create_multi_vertical (int width,
int height,
const GdkRGBA *colors,
int count);
static GdkPixbuf* meta_gradient_create_multi_diagonal (int width,
int height,
const GdkRGBA *colors,
int count);
/* Used as the destroy notification function for gdk_pixbuf_new() */
static void
free_buffer (guchar *pixels, gpointer data)
{
g_free (pixels);
}
static GdkPixbuf*
blank_pixbuf (int width, int height)
{
guchar *buf;
int rowstride;
g_return_val_if_fail (width > 0, NULL);
g_return_val_if_fail (height > 0, NULL);
/* Always align rows to 32-bit boundaries */
rowstride = 4 * ((4 * width + 4) / 4);
buf = g_try_malloc (height * rowstride);
if (!buf)
return NULL;
return gdk_pixbuf_new_from_data (buf, GDK_COLORSPACE_RGB,
TRUE, 8,
width, height, rowstride,
free_buffer, NULL);
}
/**
* meta_gradient_create_simple:
* @width: Width in pixels
* @height: Height in pixels
* @from: Starting color
* @to: Ending color
* @style: Gradient style
*
* Returns: (transfer full): A new linear gradient
*/
GdkPixbuf*
meta_gradient_create_simple (int width,
int height,
const GdkRGBA *from,
const GdkRGBA *to,
MetaGradientType style)
{
switch (style)
{
case META_GRADIENT_HORIZONTAL:
return meta_gradient_create_horizontal (width, height,
from, to);
case META_GRADIENT_VERTICAL:
return meta_gradient_create_vertical (width, height,
from, to);
case META_GRADIENT_DIAGONAL:
return meta_gradient_create_diagonal (width, height,
from, to);
case META_GRADIENT_LAST:
break;
}
g_assert_not_reached ();
return NULL;
}
/**
* meta_gradient_create_multi:
* @width: Width in pixels
* @height: Height in pixels
* @colors: (array length=n_colors): Array of colors
* @n_colors: Number of colors
* @style: Gradient style
*
* Returns: (transfer full): A new multi-step linear gradient
*/
GdkPixbuf*
meta_gradient_create_multi (int width,
int height,
const GdkRGBA *colors,
int n_colors,
MetaGradientType style)
{
if (n_colors > 2)
{
switch (style)
{
case META_GRADIENT_HORIZONTAL:
return meta_gradient_create_multi_horizontal (width, height, colors, n_colors);
case META_GRADIENT_VERTICAL:
return meta_gradient_create_multi_vertical (width, height, colors, n_colors);
case META_GRADIENT_DIAGONAL:
return meta_gradient_create_multi_diagonal (width, height, colors, n_colors);
case META_GRADIENT_LAST:
g_assert_not_reached ();
break;
}
}
else if (n_colors > 1)
{
return meta_gradient_create_simple (width, height, &colors[0], &colors[1],
style);
}
else if (n_colors > 0)
{
return meta_gradient_create_simple (width, height, &colors[0], &colors[0],
style);
}
g_assert_not_reached ();
return NULL;
}
/**
* meta_gradient_create_interwoven: (skip)
* @width: Width in pixels
* @height: Height in pixels
* @colors1: Array of colors
* @thickness1: Thickness
* @colors2: Array of colors
* @thickness2: Thickness
*
* Interwoven essentially means we have two vertical gradients,
* cut into horizontal strips of the given thickness, and then the strips
* are alternated. I'm not sure what it's good for, just copied since
* WindowMaker had it.
*/
GdkPixbuf*
meta_gradient_create_interwoven (int width,
int height,
const GdkRGBA colors1[2],
int thickness1,
const GdkRGBA colors2[2],
int thickness2)
{
int i, j, k, l, ll;
long r1, g1, b1, a1, dr1, dg1, db1, da1;
long r2, g2, b2, a2, dr2, dg2, db2, da2;
GdkPixbuf *pixbuf;
unsigned char *ptr;
unsigned char *pixels;
int rowstride;
pixbuf = blank_pixbuf (width, height);
if (pixbuf == NULL)
return NULL;
pixels = gdk_pixbuf_get_pixels (pixbuf);
rowstride = gdk_pixbuf_get_rowstride (pixbuf);
r1 = (long)(colors1[0].red*0xffffff);
g1 = (long)(colors1[0].green*0xffffff);
b1 = (long)(colors1[0].blue*0xffffff);
a1 = (long)(colors1[0].alpha*0xffffff);
r2 = (long)(colors2[0].red*0xffffff);
g2 = (long)(colors2[0].green*0xffffff);
b2 = (long)(colors2[0].blue*0xffffff);
a2 = (long)(colors2[0].alpha*0xffffff);
dr1 = ((colors1[1].red-colors1[0].red)*0xffffff)/(int)height;
dg1 = ((colors1[1].green-colors1[0].green)*0xffffff)/(int)height;
db1 = ((colors1[1].blue-colors1[0].blue)*0xffffff)/(int)height;
da1 = ((colors1[1].alpha-colors1[0].alpha)*0xffffff)/(int)height;
dr2 = ((colors2[1].red-colors2[0].red)*0xffffff)/(int)height;
dg2 = ((colors2[1].green-colors2[0].green)*0xffffff)/(int)height;
db2 = ((colors2[1].blue-colors2[0].blue)*0xffffff)/(int)height;
da2 = ((colors2[1].alpha-colors2[0].alpha)*0xffffff)/(int)height;
for (i=0,k=0,l=0,ll=thickness1; i<height; i++)
{
ptr = pixels + i * rowstride;
if (k == 0)
{
ptr[0] = (unsigned char) (r1>>16);
ptr[1] = (unsigned char) (g1>>16);
ptr[2] = (unsigned char) (b1>>16);
ptr[3] = (unsigned char) (a1>>16);
}
else
{
ptr[0] = (unsigned char) (r2>>16);
ptr[1] = (unsigned char) (g2>>16);
ptr[2] = (unsigned char) (b2>>16);
ptr[3] = (unsigned char) (a2>>16);
}
for (j=1; j <= width/2; j *= 2)
memcpy (&(ptr[j*4]), ptr, j*4);
memcpy (&(ptr[j*4]), ptr, (width - j)*4);
if (++l == ll)
{
if (k == 0)
{
k = 1;
ll = thickness2;
}
else
{
k = 0;
ll = thickness1;
}
l = 0;
}
r1+=dr1;
g1+=dg1;
b1+=db1;
a1+=da1;
r2+=dr2;
g2+=dg2;
b2+=db2;
a2+=da2;
}
return pixbuf;
}
/*
*----------------------------------------------------------------------
* meta_gradient_create_horizontal--
* Renders a horizontal linear gradient of the specified size in the
* GdkPixbuf format with a border of the specified type.
*
* Returns:
* A 24bit GdkPixbuf with the gradient (no alpha channel).
*
* Side effects:
* None
*----------------------------------------------------------------------
*/
static GdkPixbuf*
meta_gradient_create_horizontal (int width, int height,
const GdkRGBA *from,
const GdkRGBA *to)
{
int i;
long r, g, b, a, dr, dg, db, da;
GdkPixbuf *pixbuf;
unsigned char *ptr;
unsigned char *pixels;
int r0, g0, b0, a0;
int rf, gf, bf, af;
int rowstride;
pixbuf = blank_pixbuf (width, height);
if (pixbuf == NULL)
return NULL;
pixels = gdk_pixbuf_get_pixels (pixbuf);
ptr = pixels;
rowstride = gdk_pixbuf_get_rowstride (pixbuf);
r0 = (guchar) (from->red * 0xff);
g0 = (guchar) (from->green * 0xff);
b0 = (guchar) (from->blue * 0xff);
a0 = (guchar) (from->alpha * 0xff);
rf = (guchar) (to->red * 0xff);
gf = (guchar) (to->green * 0xff);
bf = (guchar) (to->blue * 0xff);
af = (guchar) (to->alpha * 0xff);
r = r0 << 16;
g = g0 << 16;
b = b0 << 16;
a = a0 << 16;
dr = ((rf-r0)<<16)/(int)width;
dg = ((gf-g0)<<16)/(int)width;
db = ((bf-b0)<<16)/(int)width;
da = ((af-a0)<<16)/(int)width;
/* render the first line */
for (i=0; i<width; i++)
{
*(ptr++) = (unsigned char)(r>>16);
*(ptr++) = (unsigned char)(g>>16);
*(ptr++) = (unsigned char)(b>>16);
*(ptr++) = (unsigned char)(a>>16);
r += dr;
g += dg;
b += db;
a += da;
}
/* copy the first line to the other lines */
for (i=1; i<height; i++)
{
memcpy (&(pixels[i*rowstride]), pixels, rowstride);
}
return pixbuf;
}
/*
*----------------------------------------------------------------------
* meta_gradient_create_vertical--
* Renders a vertical linear gradient of the specified size in the
* GdkPixbuf format with a border of the specified type.
*
* Returns:
* A 24bit GdkPixbuf with the gradient (no alpha channel).
*
* Side effects:
* None
*----------------------------------------------------------------------
*/
static GdkPixbuf*
meta_gradient_create_vertical (int width, int height,
const GdkRGBA *from,
const GdkRGBA *to)
{
int i, j;
long r, g, b, a, dr, dg, db, da;
GdkPixbuf *pixbuf;
unsigned char *ptr;
int r0, g0, b0, a0;
int rf, gf, bf, af;
int rowstride;
unsigned char *pixels;
pixbuf = blank_pixbuf (width, height);
if (pixbuf == NULL)
return NULL;
pixels = gdk_pixbuf_get_pixels (pixbuf);
rowstride = gdk_pixbuf_get_rowstride (pixbuf);
r0 = (guchar) (from->red * 0xff);
g0 = (guchar) (from->green * 0xff);
b0 = (guchar) (from->blue * 0xff);
a0 = (guchar) (from->alpha * 0xff);
rf = (guchar) (to->red * 0xff);
gf = (guchar) (to->green * 0xff);
bf = (guchar) (to->blue * 0xff);
af = (guchar) (to->alpha * 0xff);
r = r0<<16;
g = g0<<16;
b = b0<<16;
a = a0<<16;
dr = ((rf-r0)<<16)/(int)height;
dg = ((gf-g0)<<16)/(int)height;
db = ((bf-b0)<<16)/(int)height;
da = ((af-a0)<<16)/(int)height;
for (i=0; i<height; i++)
{
ptr = pixels + i * rowstride;
ptr[0] = (unsigned char)(r>>16);
ptr[1] = (unsigned char)(g>>16);
ptr[2] = (unsigned char)(b>>16);
ptr[3] = (unsigned char)(a>>16);
for (j=1; j <= width/2; j *= 2)
memcpy (&(ptr[j*4]), ptr, j*4);
memcpy (&(ptr[j*4]), ptr, (width - j)*4);
r+=dr;
g+=dg;
b+=db;
a+=da;
}
return pixbuf;
}
/*
*----------------------------------------------------------------------
* meta_gradient_create_diagonal--
* Renders a diagonal linear gradient of the specified size in the
* GdkPixbuf format with a border of the specified type.
*
* Returns:
* A 24bit GdkPixbuf with the gradient (no alpha channel).
*
* Side effects:
* None
*----------------------------------------------------------------------
*/
static GdkPixbuf*
meta_gradient_create_diagonal (int width, int height,
const GdkRGBA *from,
const GdkRGBA *to)
{
GdkPixbuf *pixbuf, *tmp;
int j;
float a, offset;
unsigned char *ptr;
unsigned char *pixels;
int rowstride;
if (width == 1)
return meta_gradient_create_vertical (width, height, from, to);
else if (height == 1)
return meta_gradient_create_horizontal (width, height, from, to);
pixbuf = blank_pixbuf (width, height);
if (pixbuf == NULL)
return NULL;
pixels = gdk_pixbuf_get_pixels (pixbuf);
rowstride = gdk_pixbuf_get_rowstride (pixbuf);
tmp = meta_gradient_create_horizontal (2*width-1, 1, from, to);
if (!tmp)
{
g_object_unref (G_OBJECT (pixbuf));
return NULL;
}
ptr = gdk_pixbuf_get_pixels (tmp);
a = ((float)(width - 1))/((float)(height - 1));
width = width * 4;
/* copy the first line to the other lines with corresponding offset */
for (j=0, offset=0.0; j<rowstride*height; j += rowstride)
{
memcpy (&(pixels[j]), &ptr[4*(int)offset], width);
offset += a;
}
g_object_unref (G_OBJECT (tmp));
return pixbuf;
}
static GdkPixbuf*
meta_gradient_create_multi_horizontal (int width, int height,
const GdkRGBA *colors,
int count)
{
int i, j, k;
long r, g, b, a, dr, dg, db, da;
GdkPixbuf *pixbuf;
unsigned char *ptr;
unsigned char *pixels;
int width2;
int rowstride;
g_return_val_if_fail (count > 2, NULL);
pixbuf = blank_pixbuf (width, height);
if (pixbuf == NULL)
return NULL;
pixels = gdk_pixbuf_get_pixels (pixbuf);
rowstride = gdk_pixbuf_get_rowstride (pixbuf);
ptr = pixels;
if (count > width)
count = width;
if (count > 1)
width2 = width/(count-1);
else
width2 = width;
k = 0;
r = (long)(colors[0].red * 0xffffff);
g = (long)(colors[0].green * 0xffffff);
b = (long)(colors[0].blue * 0xffffff);
a = (long)(colors[0].alpha * 0xffffff);
/* render the first line */
for (i=1; i<count; i++)
{
dr = (int)((colors[i].red - colors[i-1].red) *0xffffff)/(int)width2;
dg = (int)((colors[i].green - colors[i-1].green)*0xffffff)/(int)width2;
db = (int)((colors[i].blue - colors[i-1].blue) *0xffffff)/(int)width2;
da = (int)((colors[i].alpha - colors[i-1].alpha) *0xffffff)/(int)width2;
for (j=0; j<width2; j++)
{
*ptr++ = (unsigned char)(r>>16);
*ptr++ = (unsigned char)(g>>16);
*ptr++ = (unsigned char)(b>>16);
*ptr++ = (unsigned char)(a>>16);
r += dr;
g += dg;
b += db;
a += da;
k++;
}
r = (long)(colors[i].red * 0xffffff);
g = (long)(colors[i].green * 0xffffff);
b = (long)(colors[i].blue * 0xffffff);
a = (long)(colors[i].alpha * 0xffffff);
}
for (j=k; j<width; j++)
{
*ptr++ = (unsigned char)(r>>16);
*ptr++ = (unsigned char)(g>>16);
*ptr++ = (unsigned char)(b>>16);
*ptr++ = (unsigned char)(a>>16);
}
/* copy the first line to the other lines */
for (i=1; i<height; i++)
{
memcpy (&(pixels[i*rowstride]), pixels, rowstride);
}
return pixbuf;
}
static GdkPixbuf*
meta_gradient_create_multi_vertical (int width, int height,
const GdkRGBA *colors,
int count)
{
int i, j, k;
long r, g, b, a, dr, dg, db, da;
GdkPixbuf *pixbuf;
unsigned char *ptr, *tmp, *pixels;
int height2;
int x;
int rowstride;
g_return_val_if_fail (count > 2, NULL);
pixbuf = blank_pixbuf (width, height);
if (pixbuf == NULL)
return NULL;
pixels = gdk_pixbuf_get_pixels (pixbuf);
rowstride = gdk_pixbuf_get_rowstride (pixbuf);
ptr = pixels;
if (count > height)
count = height;
if (count > 1)
height2 = height/(count-1);
else
height2 = height;
k = 0;
r = (long)(colors[0].red * 0xffffff);
g = (long)(colors[0].green * 0xffffff);
b = (long)(colors[0].blue * 0xffffff);
a = (long)(colors[0].alpha * 0xffffff);
for (i=1; i<count; i++)
{
dr = (int)((colors[i].red - colors[i-1].red) *0xffffff)/(int)height2;
dg = (int)((colors[i].green - colors[i-1].green)*0xffffff)/(int)height2;
db = (int)((colors[i].blue - colors[i-1].blue) *0xffffff)/(int)height2;
da = (int)((colors[i].alpha - colors[i-1].alpha) *0xffffff)/(int)height2;
for (j=0; j<height2; j++)
{
ptr[0] = (unsigned char)(r>>16);
ptr[1] = (unsigned char)(g>>16);
ptr[2] = (unsigned char)(b>>16);
ptr[3] = (unsigned char)(a>>16);
for (x=1; x <= width/2; x *= 2)
memcpy (&(ptr[x*4]), ptr, x*4);
memcpy (&(ptr[x*4]), ptr, (width - x)*4);
ptr += rowstride;
r += dr;
g += dg;
b += db;
a += da;
k++;
}
r = (long)(colors[i].red * 0xffffff);
g = (long)(colors[i].green * 0xffffff);
b = (long)(colors[i].blue * 0xffffff);
a = (long)(colors[i].alpha * 0xffffff);
}
if (k<height)
{
tmp = ptr;
ptr[0] = (unsigned char) (r>>16);
ptr[1] = (unsigned char) (g>>16);
ptr[2] = (unsigned char) (b>>16);
ptr[3] = (unsigned char) (a>>16);
for (x=1; x <= width/2; x *= 2)
memcpy (&(ptr[x*4]), ptr, x*4);
memcpy (&(ptr[x*4]), ptr, (width - x)*4);
ptr += rowstride;
for (j=k+1; j<height; j++)
{
memcpy (ptr, tmp, rowstride);
ptr += rowstride;
}
}
return pixbuf;
}
static GdkPixbuf*
meta_gradient_create_multi_diagonal (int width, int height,
const GdkRGBA *colors,
int count)
{
GdkPixbuf *pixbuf, *tmp;
float a, offset;
int j;
unsigned char *ptr;
unsigned char *pixels;
int rowstride;
g_return_val_if_fail (count > 2, NULL);
if (width == 1)
return meta_gradient_create_multi_vertical (width, height, colors, count);
else if (height == 1)
return meta_gradient_create_multi_horizontal (width, height, colors, count);
pixbuf = gdk_pixbuf_new (GDK_COLORSPACE_RGB, FALSE, 8,
width, height);
if (pixbuf == NULL)
return NULL;
pixels = gdk_pixbuf_get_pixels (pixbuf);
rowstride = gdk_pixbuf_get_rowstride (pixbuf);
if (count > width)
count = width;
if (count > height)
count = height;
if (count > 2)
tmp = meta_gradient_create_multi_horizontal (2*width-1, 1, colors, count);
else
/* wrlib multiplies these colors by 256 before passing them in, but
* I think it's a bug in wrlib, so changed here. I could be wrong
* though, if we notice two-color multi diagonals not working.
*/
tmp = meta_gradient_create_horizontal (2*width-1, 1,
&colors[0], &colors[1]);
if (!tmp)
{
g_object_unref (G_OBJECT (pixbuf));
return NULL;
}
ptr = gdk_pixbuf_get_pixels (tmp);
a = ((float)(width - 1))/((float)(height - 1));
width = width * 3;
/* copy the first line to the other lines with corresponding offset */
for (j=0, offset=0; j<rowstride*height; j += rowstride)
{
memcpy (&(pixels[j]), &ptr[3*(int)offset], width);
offset += a;
}
g_object_unref (G_OBJECT (tmp));
return pixbuf;
}
static void
simple_multiply_alpha (GdkPixbuf *pixbuf,
guchar alpha)
{
guchar *pixels;
int rowstride;
int height;
int row;
g_return_if_fail (GDK_IS_PIXBUF (pixbuf));
if (alpha == 255)
return;
g_assert (gdk_pixbuf_get_has_alpha (pixbuf));
pixels = gdk_pixbuf_get_pixels (pixbuf);
rowstride = gdk_pixbuf_get_rowstride (pixbuf);
height = gdk_pixbuf_get_height (pixbuf);
row = 0;
while (row < height)
{
guchar *p;
guchar *end;
p = pixels + row * rowstride;
end = p + rowstride;
while (p != end)
{
p += 3; /* skip RGB */
/* multiply the two alpha channels. not sure this is right.
* but some end cases are that if the pixbuf contains 255,
* then it should be modified to contain "alpha"; if the
* pixbuf contains 0, it should remain 0.
*/
/* ((*p / 255.0) * (alpha / 255.0)) * 255; */
*p = (guchar) (((int) *p * (int) alpha) / (int) 255);
++p; /* skip A */
}
++row;
}
}
static void
meta_gradient_add_alpha_horizontal (GdkPixbuf *pixbuf,
const unsigned char *alphas,
int n_alphas)
{
int i, j;
long a, da;
unsigned char *p;
unsigned char *pixels;
int width2;
int rowstride;
int width, height;
unsigned char *gradient;
unsigned char *gradient_p;
unsigned char *gradient_end;
g_return_if_fail (n_alphas > 0);
if (n_alphas == 1)
{
/* Optimize this */
simple_multiply_alpha (pixbuf, alphas[0]);
return;
}
width = gdk_pixbuf_get_width (pixbuf);
height = gdk_pixbuf_get_height (pixbuf);
gradient = g_new (unsigned char, width);
gradient_end = gradient + width;
if (n_alphas > width)
n_alphas = width;
if (n_alphas > 1)
width2 = width / (n_alphas - 1);
else
width2 = width;
a = alphas[0] << 8;
gradient_p = gradient;
/* render the gradient into an array */
for (i = 1; i < n_alphas; i++)
{
da = (((int)(alphas[i] - (int) alphas[i-1])) << 8) / (int) width2;
for (j = 0; j < width2; j++)
{
*gradient_p++ = (a >> 8);
a += da;
}
a = alphas[i] << 8;
}
/* get leftover pixels */
while (gradient_p != gradient_end)
{
*gradient_p++ = a >> 8;
}
/* Now for each line of the pixbuf, fill in with the gradient */
pixels = gdk_pixbuf_get_pixels (pixbuf);
rowstride = gdk_pixbuf_get_rowstride (pixbuf);
p = pixels;
i = 0;
while (i < height)
{
unsigned char *row_end = p + rowstride;
gradient_p = gradient;
p += 3;
while (gradient_p != gradient_end)
{
/* multiply the two alpha channels. not sure this is right.
* but some end cases are that if the pixbuf contains 255,
* then it should be modified to contain "alpha"; if the
* pixbuf contains 0, it should remain 0.
*/
/* ((*p / 255.0) * (alpha / 255.0)) * 255; */
*p = (guchar) (((int) *p * (int) *gradient_p) / (int) 255);
p += 4;
++gradient_p;
}
p = row_end;
++i;
}
g_free (gradient);
}
void
meta_gradient_add_alpha (GdkPixbuf *pixbuf,
const guchar *alphas,
int n_alphas,
MetaGradientType type)
{
g_return_if_fail (GDK_IS_PIXBUF (pixbuf));
g_return_if_fail (gdk_pixbuf_get_has_alpha (pixbuf));
g_return_if_fail (n_alphas > 0);
switch (type)
{
case META_GRADIENT_HORIZONTAL:
meta_gradient_add_alpha_horizontal (pixbuf, alphas, n_alphas);
break;
case META_GRADIENT_VERTICAL:
g_printerr ("metacity: vertical alpha channel gradient not implemented yet\n");
break;
case META_GRADIENT_DIAGONAL:
g_printerr ("metacity: diagonal alpha channel gradient not implemented yet\n");
break;
case META_GRADIENT_LAST:
g_assert_not_reached ();
break;
}
}

View File

@@ -1,315 +0,0 @@
/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
/* Mutter gradient test program */
/*
* Copyright (C) 2002 Havoc Pennington
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License as
* published by the Free Software Foundation; either version 2 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, see <http://www.gnu.org/licenses/>. */
#include <meta/gradient.h>
#include <gtk/gtk.h>
typedef void (* RenderGradientFunc) (cairo_t *cr,
int width,
int height);
static void
draw_checkerboard (cairo_t *cr,
int width,
int height)
{
gint i, j, xcount, ycount;
GdkRGBA color1, color2;
#define CHECK_SIZE 10
#define SPACING 2
color1.red = 30000. / 65535.;
color1.green = 30000. / 65535.;
color1.blue = 30000. / 65535.;
color1.alpha = 1.0;
color2.red = 50000. / 65535.;
color2.green = 50000. / 65535.;
color2.blue = 50000. / 65535.;
color2.alpha = 1.0;
xcount = 0;
i = SPACING;
while (i < width)
{
j = SPACING;
ycount = xcount % 2; /* start with even/odd depending on row */
while (j < height)
{
if (ycount % 2)
gdk_cairo_set_source_rgba (cr, &color1);
else
gdk_cairo_set_source_rgba (cr, &color2);
/* If we're outside event->area, this will do nothing.
* It might be mildly more efficient if we handled
* the clipping ourselves, but again we're feeling lazy.
*/
cairo_rectangle (cr, i, j, CHECK_SIZE, CHECK_SIZE);
cairo_fill (cr);
j += CHECK_SIZE + SPACING;
++ycount;
}
i += CHECK_SIZE + SPACING;
++xcount;
}
}
static void
render_simple (cairo_t *cr,
int width, int height,
MetaGradientType type,
gboolean with_alpha)
{
GdkPixbuf *pixbuf;
GdkRGBA from, to;
gdk_rgba_parse (&from, "blue");
gdk_rgba_parse (&to, "green");
pixbuf = meta_gradient_create_simple (width, height,
&from, &to,
type);
if (with_alpha)
{
const unsigned char alphas[] = { 0xff, 0xaa, 0x2f, 0x0, 0xcc, 0xff, 0xff };
if (!gdk_pixbuf_get_has_alpha (pixbuf))
{
GdkPixbuf *new_pixbuf;
new_pixbuf = gdk_pixbuf_add_alpha (pixbuf, FALSE, 0, 0, 0);
g_object_unref (G_OBJECT (pixbuf));
pixbuf = new_pixbuf;
}
meta_gradient_add_alpha (pixbuf,
alphas, G_N_ELEMENTS (alphas),
META_GRADIENT_HORIZONTAL);
draw_checkerboard (cr , width, height);
}
gdk_cairo_set_source_pixbuf (cr, pixbuf, 0, 0);
cairo_rectangle (cr, 0, 0, width, height);
cairo_fill (cr);
g_object_unref (G_OBJECT (pixbuf));
}
static void
render_vertical_func (cairo_t *cr,
int width, int height)
{
render_simple (cr, width, height, META_GRADIENT_VERTICAL, FALSE);
}
static void
render_horizontal_func (cairo_t *cr,
int width, int height)
{
render_simple (cr, width, height, META_GRADIENT_HORIZONTAL, FALSE);
}
static void
render_diagonal_func (cairo_t *cr,
int width, int height)
{
render_simple (cr, width, height, META_GRADIENT_DIAGONAL, FALSE);
}
static void
render_diagonal_alpha_func (cairo_t *cr,
int width, int height)
{
render_simple (cr, width, height, META_GRADIENT_DIAGONAL, TRUE);
}
static void
render_multi (cairo_t *cr,
int width, int height,
MetaGradientType type)
{
GdkPixbuf *pixbuf;
#define N_COLORS 5
GdkRGBA colors[N_COLORS];
gdk_rgba_parse (&colors[0], "red");
gdk_rgba_parse (&colors[1], "blue");
gdk_rgba_parse (&colors[2], "orange");
gdk_rgba_parse (&colors[3], "pink");
gdk_rgba_parse (&colors[4], "green");
pixbuf = meta_gradient_create_multi (width, height,
colors, N_COLORS,
type);
gdk_cairo_set_source_pixbuf (cr, pixbuf, 0, 0);
cairo_rectangle (cr, 0, 0, width, height);
cairo_fill (cr);
g_object_unref (G_OBJECT (pixbuf));
#undef N_COLORS
}
static void
render_vertical_multi_func (cairo_t *cr,
int width, int height)
{
render_multi (cr, width, height, META_GRADIENT_VERTICAL);
}
static void
render_horizontal_multi_func (cairo_t *cr,
int width, int height)
{
render_multi (cr, width, height, META_GRADIENT_HORIZONTAL);
}
static void
render_diagonal_multi_func (cairo_t *cr,
int width, int height)
{
render_multi (cr, width, height, META_GRADIENT_DIAGONAL);
}
static void
render_interwoven_func (cairo_t *cr,
int width, int height)
{
GdkPixbuf *pixbuf;
#define N_COLORS 4
GdkRGBA colors[N_COLORS];
gdk_rgba_parse (&colors[0], "red");
gdk_rgba_parse (&colors[1], "blue");
gdk_rgba_parse (&colors[2], "pink");
gdk_rgba_parse (&colors[3], "green");
pixbuf = meta_gradient_create_interwoven (width, height,
colors, height / 10,
colors + 2, height / 14);
gdk_cairo_set_source_pixbuf (cr, pixbuf, 0, 0);
cairo_rectangle (cr, 0, 0, width, height);
cairo_fill (cr);
g_object_unref (G_OBJECT (pixbuf));
}
static gboolean
draw_callback (GtkWidget *widget,
cairo_t *cr,
gpointer data)
{
RenderGradientFunc func = data;
GtkStyleContext *style;
GdkRGBA color;
style = gtk_widget_get_style_context (widget);
gtk_style_context_save (style);
gtk_style_context_set_state (style, gtk_widget_get_state_flags (widget));
gtk_style_context_lookup_color (style, "foreground-color", &color);
gtk_style_context_restore (style);
gdk_cairo_set_source_rgba (cr, &color);
(* func) (cr,
gtk_widget_get_allocated_width (widget),
gtk_widget_get_allocated_height (widget));
return FALSE;
}
static GtkWidget*
create_gradient_window (const char *title,
RenderGradientFunc func)
{
GtkWidget *window;
GtkWidget *drawing_area;
window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
gtk_window_set_title (GTK_WINDOW (window), title);
drawing_area = gtk_drawing_area_new ();
gtk_widget_set_size_request (drawing_area, 1, 1);
gtk_window_set_default_size (GTK_WINDOW (window), 175, 175);
g_signal_connect (G_OBJECT (drawing_area),
"draw",
G_CALLBACK (draw_callback),
func);
gtk_container_add (GTK_CONTAINER (window), drawing_area);
gtk_widget_show_all (window);
return window;
}
static void
meta_gradient_test (void)
{
create_gradient_window ("Simple vertical",
render_vertical_func);
create_gradient_window ("Simple horizontal",
render_horizontal_func);
create_gradient_window ("Simple diagonal",
render_diagonal_func);
create_gradient_window ("Multi vertical",
render_vertical_multi_func);
create_gradient_window ("Multi horizontal",
render_horizontal_multi_func);
create_gradient_window ("Multi diagonal",
render_diagonal_multi_func);
create_gradient_window ("Interwoven",
render_interwoven_func);
create_gradient_window ("Simple diagonal with horizontal multi alpha",
render_diagonal_alpha_func);
}
int
main (int argc, char **argv)
{
gtk_init (&argc, &argv);
meta_gradient_test ();
gtk_main ();
return 0;
}

View File

@@ -774,9 +774,6 @@ parse_alpha (const char *str,
n_alphas = i;
/* FIXME allow specifying horizontal/vertical/diagonal in theme format,
* once we implement vertical/diagonal in gradient.c
*/
spec = meta_alpha_gradient_spec_new (META_GRADIENT_HORIZONTAL,
n_alphas);
@@ -2150,11 +2147,9 @@ parse_draw_op_element (GMarkupParseContext *context,
const char *width;
const char *height;
const char *alpha;
const char *colorize;
const char *fill_type;
MetaAlphaGradientSpec *alpha_spec;
GdkPixbuf *pixbuf;
MetaColorSpec *colorize_spec = NULL;
MetaImageFillType fill_type_val;
int h, w, c;
int pixbuf_width, pixbuf_height, pixbuf_n_channels, pixbuf_rowstride;
@@ -2165,7 +2160,6 @@ parse_draw_op_element (GMarkupParseContext *context,
"!x", &x, "!y", &y,
"!width", &width, "!height", &height,
"alpha", &alpha, "!filename", &filename,
"colorize", &colorize,
"fill_type", &fill_type,
NULL))
return;
@@ -2211,18 +2205,6 @@ parse_draw_op_element (GMarkupParseContext *context,
return;
}
if (colorize)
{
colorize_spec = parse_color (info->theme, colorize, error);
if (colorize_spec == NULL)
{
add_context_to_error (error, context);
g_object_unref (G_OBJECT (pixbuf));
return;
}
}
alpha_spec = NULL;
if (alpha && !parse_alpha (alpha, &alpha_spec, context, error))
{
@@ -2233,7 +2215,6 @@ parse_draw_op_element (GMarkupParseContext *context,
op = meta_draw_op_new (META_DRAW_IMAGE);
op->data.image.pixbuf = pixbuf;
op->data.image.colorize_spec = colorize_spec;
op->data.image.x = meta_draw_spec_new (info->theme, x, NULL);
op->data.image.y = meta_draw_spec_new (info->theme, y, NULL);

View File

@@ -23,7 +23,6 @@
#define META_THEME_PRIVATE_H
#include <meta/boxes.h>
#include <meta/gradient.h>
#include <meta/theme.h>
#include <meta/common.h>
#include <gtk/gtk.h>
@@ -273,6 +272,14 @@ typedef enum
META_IMAGE_FILL_TILE
} MetaImageFillType;
typedef enum
{
META_GRADIENT_VERTICAL,
META_GRADIENT_HORIZONTAL,
META_GRADIENT_DIAGONAL,
META_GRADIENT_LAST
} MetaGradientType;
typedef enum
{
META_COLOR_SPEC_BASIC,
@@ -538,7 +545,6 @@ struct _MetaDrawOp
} gradient;
struct {
MetaColorSpec *colorize_spec;
MetaAlphaGradientSpec *alpha_spec;
GdkPixbuf *pixbuf;
MetaDrawSpec *x;
@@ -546,8 +552,6 @@ struct _MetaDrawOp
MetaDrawSpec *width;
MetaDrawSpec *height;
guint32 colorize_cache_pixel;
GdkPixbuf *colorize_cache_pixbuf;
MetaImageFillType fill_type;
unsigned int vertical_stripes : 1;
unsigned int horizontal_stripes : 1;
@@ -972,8 +976,12 @@ gboolean meta_draw_op_list_contains (MetaDrawOpList *op_list,
MetaGradientSpec* meta_gradient_spec_new (MetaGradientType type);
void meta_gradient_spec_free (MetaGradientSpec *desc);
GdkPixbuf* meta_gradient_spec_render (const MetaGradientSpec *desc,
GtkStyleContext *gtk_style,
void meta_gradient_spec_render (const MetaGradientSpec *spec,
const MetaAlphaGradientSpec *alpha_spec,
cairo_t *cr,
GtkStyleContext *style,
int x,
int y,
int width,
int height);
gboolean meta_gradient_spec_validate (MetaGradientSpec *spec,

View File

@@ -38,7 +38,6 @@
#include "theme-private.h"
#include "frames.h" /* for META_TYPE_FRAMES */
#include "util-private.h"
#include <meta/gradient.h>
#include <meta/prefs.h>
#include <gtk/gtk.h>
#include <string.h>
@@ -75,84 +74,6 @@ static void hls_to_rgb (gdouble *h,
*/
static MetaTheme *meta_current_theme = NULL;
static GdkPixbuf *
colorize_pixbuf (GdkPixbuf *orig,
GdkRGBA *new_color)
{
GdkPixbuf *pixbuf;
double intensity;
int x, y;
const guchar *src;
guchar *dest;
int orig_rowstride;
int dest_rowstride;
int width, height;
gboolean has_alpha;
const guchar *src_pixels;
guchar *dest_pixels;
pixbuf = gdk_pixbuf_new (gdk_pixbuf_get_colorspace (orig), gdk_pixbuf_get_has_alpha (orig),
gdk_pixbuf_get_bits_per_sample (orig),
gdk_pixbuf_get_width (orig), gdk_pixbuf_get_height (orig));
if (pixbuf == NULL)
return NULL;
orig_rowstride = gdk_pixbuf_get_rowstride (orig);
dest_rowstride = gdk_pixbuf_get_rowstride (pixbuf);
width = gdk_pixbuf_get_width (pixbuf);
height = gdk_pixbuf_get_height (pixbuf);
has_alpha = gdk_pixbuf_get_has_alpha (orig);
src_pixels = gdk_pixbuf_get_pixels (orig);
dest_pixels = gdk_pixbuf_get_pixels (pixbuf);
for (y = 0; y < height; y++)
{
src = src_pixels + y * orig_rowstride;
dest = dest_pixels + y * dest_rowstride;
for (x = 0; x < width; x++)
{
double dr, dg, db;
intensity = INTENSITY (src[0], src[1], src[2]) / 255.0;
if (intensity <= 0.5)
{
/* Go from black at intensity = 0.0 to new_color at intensity = 0.5 */
dr = new_color->red * intensity * 2.0;
dg = new_color->green * intensity * 2.0;
db = new_color->blue * intensity * 2.0;
}
else
{
/* Go from new_color at intensity = 0.5 to white at intensity = 1.0 */
dr = new_color->red + (1.0 - new_color->red) * (intensity - 0.5) * 2.0;
dg = new_color->green + (1.0 - new_color->green) * (intensity - 0.5) * 2.0;
db = new_color->blue + (1.0 - new_color->blue) * (intensity - 0.5) * 2.0;
}
dest[0] = CLAMP_UCHAR (255 * dr);
dest[1] = CLAMP_UCHAR (255 * dg);
dest[2] = CLAMP_UCHAR (255 * db);
if (has_alpha)
{
dest[3] = src[3];
src += 4;
dest += 4;
}
else
{
src += 3;
dest += 3;
}
}
}
return pixbuf;
}
static void
color_composite (const GdkRGBA *bg,
const GdkRGBA *fg,
@@ -1010,42 +931,84 @@ meta_gradient_spec_free (MetaGradientSpec *spec)
g_free (spec);
}
GdkPixbuf*
meta_gradient_spec_render (const MetaGradientSpec *spec,
GtkStyleContext *style,
int width,
int height)
static cairo_pattern_t *
meta_gradient_spec_pattern (const MetaGradientSpec *spec,
const MetaAlphaGradientSpec *alpha_spec,
GtkStyleContext *style)
{
cairo_pattern_t *pattern;
int n_colors;
GdkRGBA *colors;
GSList *tmp;
GSList *l;
int i;
GdkPixbuf *pixbuf;
if (spec->type == META_GRADIENT_HORIZONTAL)
pattern = cairo_pattern_create_linear (0, 0, 1, 0);
if (spec->type == META_GRADIENT_VERTICAL)
pattern = cairo_pattern_create_linear (0, 0, 0, 1);
else if (spec->type == META_GRADIENT_DIAGONAL)
pattern = cairo_pattern_create_linear (0, 0, 1, 1);
else
g_assert_not_reached ();
n_colors = g_slist_length (spec->color_specs);
if (n_colors == 0)
return NULL;
colors = g_new (GdkRGBA, n_colors);
if (alpha_spec != NULL)
g_assert (n_colors == alpha_spec->n_alphas);
i = 0;
tmp = spec->color_specs;
while (tmp != NULL)
for (l = spec->color_specs; l != NULL; l = l->next)
{
meta_color_spec_render (tmp->data, style, &colors[i]);
MetaColorSpec *color_spec = l->data;
GdkRGBA color;
tmp = tmp->next;
meta_color_spec_render (color_spec, style, &color);
if (alpha_spec != NULL)
color.alpha *= alpha_spec->alphas[i];
cairo_pattern_add_color_stop_rgba (pattern,
i / (float) n_colors,
color.red,
color.green,
color.blue,
color.alpha);
++i;
}
pixbuf = meta_gradient_create_multi (width, height,
colors, n_colors,
spec->type);
return pattern;
}
g_free (colors);
void
meta_gradient_spec_render (const MetaGradientSpec *spec,
const MetaAlphaGradientSpec *alpha_spec,
cairo_t *cr,
GtkStyleContext *style,
int x,
int y,
int width,
int height)
{
cairo_pattern_t *pattern;
return pixbuf;
cairo_save (cr);
pattern = meta_gradient_spec_pattern (spec, alpha_spec, style);
if (pattern == NULL)
return;
cairo_rectangle (cr, x, y, width, height);
cairo_translate (cr, x, y);
cairo_scale (cr, width, height);
cairo_set_source (cr, pattern);
cairo_fill (cr);
cairo_pattern_destroy (pattern);
cairo_restore (cr);
}
gboolean
@@ -3052,12 +3015,6 @@ meta_draw_op_free (MetaDrawOp *op)
if (op->data.image.pixbuf)
g_object_unref (G_OBJECT (op->data.image.pixbuf));
if (op->data.image.colorize_spec)
meta_color_spec_free (op->data.image.colorize_spec);
if (op->data.image.colorize_cache_pixbuf)
g_object_unref (G_OBJECT (op->data.image.colorize_cache_pixbuf));
meta_draw_spec_free (op->data.image.x);
meta_draw_spec_free (op->data.image.y);
meta_draw_spec_free (op->data.image.width);
@@ -3132,413 +3089,6 @@ meta_draw_op_free (MetaDrawOp *op)
g_free (op);
}
static GdkPixbuf*
apply_alpha (GdkPixbuf *pixbuf,
MetaAlphaGradientSpec *spec,
gboolean force_copy)
{
GdkPixbuf *new_pixbuf;
gboolean needs_alpha;
g_return_val_if_fail (GDK_IS_PIXBUF (pixbuf), NULL);
needs_alpha = spec && (spec->n_alphas > 1 ||
spec->alphas[0] != 0xff);
if (!needs_alpha)
return pixbuf;
if (!gdk_pixbuf_get_has_alpha (pixbuf))
{
new_pixbuf = gdk_pixbuf_add_alpha (pixbuf, FALSE, 0, 0, 0);
g_object_unref (G_OBJECT (pixbuf));
pixbuf = new_pixbuf;
}
else if (force_copy)
{
new_pixbuf = gdk_pixbuf_copy (pixbuf);
g_object_unref (G_OBJECT (pixbuf));
pixbuf = new_pixbuf;
}
g_assert (gdk_pixbuf_get_has_alpha (pixbuf));
meta_gradient_add_alpha (pixbuf, spec->alphas, spec->n_alphas, spec->type);
return pixbuf;
}
static GdkPixbuf*
pixbuf_tile (GdkPixbuf *tile,
int width,
int height)
{
GdkPixbuf *pixbuf;
int tile_width;
int tile_height;
int i, j;
tile_width = gdk_pixbuf_get_width (tile);
tile_height = gdk_pixbuf_get_height (tile);
pixbuf = gdk_pixbuf_new (GDK_COLORSPACE_RGB,
gdk_pixbuf_get_has_alpha (tile),
8, width, height);
i = 0;
while (i < width)
{
j = 0;
while (j < height)
{
int w, h;
w = MIN (tile_width, width - i);
h = MIN (tile_height, height - j);
gdk_pixbuf_copy_area (tile,
0, 0,
w, h,
pixbuf,
i, j);
j += tile_height;
}
i += tile_width;
}
return pixbuf;
}
static GdkPixbuf *
replicate_rows (GdkPixbuf *src,
int src_x,
int src_y,
int width,
int height)
{
unsigned int n_channels = gdk_pixbuf_get_n_channels (src);
unsigned int src_rowstride = gdk_pixbuf_get_rowstride (src);
unsigned char *pixels = (gdk_pixbuf_get_pixels (src) + src_y * src_rowstride + src_x
* n_channels);
unsigned char *dest_pixels;
GdkPixbuf *result;
unsigned int dest_rowstride;
int i;
result = gdk_pixbuf_new (GDK_COLORSPACE_RGB, n_channels == 4, 8,
width, height);
dest_rowstride = gdk_pixbuf_get_rowstride (result);
dest_pixels = gdk_pixbuf_get_pixels (result);
for (i = 0; i < height; i++)
memcpy (dest_pixels + dest_rowstride * i, pixels, n_channels * width);
return result;
}
static GdkPixbuf *
replicate_cols (GdkPixbuf *src,
int src_x,
int src_y,
int width,
int height)
{
unsigned int n_channels = gdk_pixbuf_get_n_channels (src);
unsigned int src_rowstride = gdk_pixbuf_get_rowstride (src);
unsigned char *pixels = (gdk_pixbuf_get_pixels (src) + src_y * src_rowstride + src_x
* n_channels);
unsigned char *dest_pixels;
GdkPixbuf *result;
unsigned int dest_rowstride;
int i, j;
result = gdk_pixbuf_new (GDK_COLORSPACE_RGB, n_channels == 4, 8,
width, height);
dest_rowstride = gdk_pixbuf_get_rowstride (result);
dest_pixels = gdk_pixbuf_get_pixels (result);
for (i = 0; i < height; i++)
{
unsigned char *p = dest_pixels + dest_rowstride * i;
unsigned char *q = pixels + src_rowstride * i;
unsigned char r = *(q++);
unsigned char g = *(q++);
unsigned char b = *(q++);
if (n_channels == 4)
{
unsigned char a;
a = *(q++);
for (j = 0; j < width; j++)
{
*(p++) = r;
*(p++) = g;
*(p++) = b;
*(p++) = a;
}
}
else
{
for (j = 0; j < width; j++)
{
*(p++) = r;
*(p++) = g;
*(p++) = b;
}
}
}
return result;
}
static GdkPixbuf*
scale_and_alpha_pixbuf (GdkPixbuf *src,
MetaAlphaGradientSpec *alpha_spec,
MetaImageFillType fill_type,
int width,
int height,
gboolean vertical_stripes,
gboolean horizontal_stripes)
{
GdkPixbuf *pixbuf;
GdkPixbuf *temp_pixbuf;
pixbuf = NULL;
pixbuf = src;
if (gdk_pixbuf_get_width (pixbuf) == width &&
gdk_pixbuf_get_height (pixbuf) == height)
{
g_object_ref (G_OBJECT (pixbuf));
}
else
{
if (fill_type == META_IMAGE_FILL_TILE)
{
pixbuf = pixbuf_tile (pixbuf, width, height);
}
else
{
int src_h, src_w, dest_h, dest_w;
src_h = gdk_pixbuf_get_height (src);
src_w = gdk_pixbuf_get_width (src);
/* prefer to replicate_cols if possible, as that
* is faster (no memory reads)
*/
if (horizontal_stripes)
{
dest_w = gdk_pixbuf_get_width (src);
dest_h = height;
}
else if (vertical_stripes)
{
dest_w = width;
dest_h = gdk_pixbuf_get_height (src);
}
else
{
dest_w = width;
dest_h = height;
}
if (dest_w == src_w && dest_h == src_h)
{
temp_pixbuf = src;
g_object_ref (G_OBJECT (temp_pixbuf));
}
else
{
temp_pixbuf = gdk_pixbuf_scale_simple (src,
dest_w, dest_h,
GDK_INTERP_BILINEAR);
}
/* prefer to replicate_cols if possible, as that
* is faster (no memory reads)
*/
if (horizontal_stripes)
{
pixbuf = replicate_cols (temp_pixbuf, 0, 0, width, height);
g_object_unref (G_OBJECT (temp_pixbuf));
}
else if (vertical_stripes)
{
pixbuf = replicate_rows (temp_pixbuf, 0, 0, width, height);
g_object_unref (G_OBJECT (temp_pixbuf));
}
else
{
pixbuf = temp_pixbuf;
}
}
}
if (pixbuf)
pixbuf = apply_alpha (pixbuf, alpha_spec, pixbuf == src);
return pixbuf;
}
static GdkPixbuf*
draw_op_as_pixbuf (const MetaDrawOp *op,
GtkStyleContext *context,
const MetaDrawInfo *info,
int width,
int height)
{
/* Try to get the op as a pixbuf, assuming w/h in the op
* matches the width/height passed in. return NULL
* if the op can't be converted to an equivalent pixbuf.
*/
GdkPixbuf *pixbuf;
pixbuf = NULL;
switch (op->type)
{
case META_DRAW_TINT:
{
GdkRGBA color;
guint32 rgba;
gboolean has_alpha;
meta_color_spec_render (op->data.rectangle.color_spec,
context,
&color);
has_alpha =
op->data.tint.alpha_spec &&
(op->data.tint.alpha_spec->n_alphas > 1 ||
op->data.tint.alpha_spec->alphas[0] != 0xff);
pixbuf = gdk_pixbuf_new (GDK_COLORSPACE_RGB,
has_alpha,
8, width, height);
if (!has_alpha)
{
rgba = GDK_COLOR_RGBA (color);
gdk_pixbuf_fill (pixbuf, rgba);
}
else if (op->data.tint.alpha_spec->n_alphas == 1)
{
rgba = GDK_COLOR_RGBA (color);
rgba &= ~0xff;
rgba |= op->data.tint.alpha_spec->alphas[0];
gdk_pixbuf_fill (pixbuf, rgba);
}
else
{
rgba = GDK_COLOR_RGBA (color);
gdk_pixbuf_fill (pixbuf, rgba);
meta_gradient_add_alpha (pixbuf,
op->data.tint.alpha_spec->alphas,
op->data.tint.alpha_spec->n_alphas,
op->data.tint.alpha_spec->type);
}
}
break;
case META_DRAW_GRADIENT:
{
pixbuf = meta_gradient_spec_render (op->data.gradient.gradient_spec,
context, width, height);
pixbuf = apply_alpha (pixbuf,
op->data.gradient.alpha_spec,
FALSE);
}
break;
case META_DRAW_IMAGE:
{
if (op->data.image.colorize_spec)
{
GdkRGBA color;
meta_color_spec_render (op->data.image.colorize_spec,
context, &color);
if (op->data.image.colorize_cache_pixbuf == NULL ||
op->data.image.colorize_cache_pixel != GDK_COLOR_RGB (color))
{
if (op->data.image.colorize_cache_pixbuf)
g_object_unref (G_OBJECT (op->data.image.colorize_cache_pixbuf));
/* const cast here */
((MetaDrawOp*)op)->data.image.colorize_cache_pixbuf =
colorize_pixbuf (op->data.image.pixbuf,
&color);
((MetaDrawOp*)op)->data.image.colorize_cache_pixel =
GDK_COLOR_RGB (color);
}
if (op->data.image.colorize_cache_pixbuf)
{
pixbuf = scale_and_alpha_pixbuf (op->data.image.colorize_cache_pixbuf,
op->data.image.alpha_spec,
op->data.image.fill_type,
width, height,
op->data.image.vertical_stripes,
op->data.image.horizontal_stripes);
}
}
else
{
pixbuf = scale_and_alpha_pixbuf (op->data.image.pixbuf,
op->data.image.alpha_spec,
op->data.image.fill_type,
width, height,
op->data.image.vertical_stripes,
op->data.image.horizontal_stripes);
}
break;
}
case META_DRAW_ICON:
if (info->mini_icon &&
width <= gdk_pixbuf_get_width (info->mini_icon) &&
height <= gdk_pixbuf_get_height (info->mini_icon))
pixbuf = scale_and_alpha_pixbuf (info->mini_icon,
op->data.icon.alpha_spec,
op->data.icon.fill_type,
width, height,
FALSE, FALSE);
else if (info->icon)
pixbuf = scale_and_alpha_pixbuf (info->icon,
op->data.icon.alpha_spec,
op->data.icon.fill_type,
width, height,
FALSE, FALSE);
break;
case META_DRAW_LINE:
case META_DRAW_RECTANGLE:
case META_DRAW_ARC:
case META_DRAW_CLIP:
case META_DRAW_GTK_ARROW:
case META_DRAW_GTK_BOX:
case META_DRAW_GTK_VLINE:
case META_DRAW_TITLE:
case META_DRAW_OP_LIST:
case META_DRAW_TILE:
break;
}
return pixbuf;
}
static void
fill_env (MetaPositionExprEnv *env,
const MetaDrawInfo *info,
@@ -3578,6 +3128,86 @@ fill_env (MetaPositionExprEnv *env,
env->theme = meta_current_theme;
}
static cairo_pattern_t *
meta_alpha_gradient_spec_pattern (const MetaAlphaGradientSpec *alpha_spec)
{
/* Hardcoded in theme-parser.c */
g_assert (alpha_spec->type == META_GRADIENT_HORIZONTAL);
int n_alphas = alpha_spec->n_alphas;
if (n_alphas == 0)
return NULL;
else if (n_alphas == 1)
return cairo_pattern_create_rgba (0, 0, 0, alpha_spec->alphas[0]);
else
{
cairo_pattern_t *pattern = cairo_pattern_create_linear (0, 0, 1, 0);
int i;
for (i = 0; i < n_alphas; i++)
cairo_pattern_add_color_stop_rgba (pattern,
i / (float) n_alphas,
0, 0, 0, alpha_spec->alphas[i]);
return pattern;
}
}
static void
draw_image (cairo_t *cr,
GdkPixbuf *src,
MetaImageFillType fill_type,
MetaAlphaGradientSpec *alpha_spec,
int x,
int y,
int width,
int height)
{
cairo_save (cr);
cairo_rectangle (cr, x, y, width, height);
if (fill_type == META_IMAGE_FILL_TILE)
{
gdk_cairo_set_source_pixbuf (cr, src, 0, 0);
cairo_pattern_set_extend (cairo_get_source (cr),
CAIRO_EXTEND_REPEAT);
}
else
{
float pixbuf_width, pixbuf_height;
pixbuf_width = gdk_pixbuf_get_width (src);
pixbuf_height = gdk_pixbuf_get_height (src);
cairo_save (cr);
cairo_translate (cr, x, y);
cairo_scale (cr,
pixbuf_width / width,
pixbuf_height / height);
gdk_cairo_set_source_pixbuf (cr, src, 0, 0);
cairo_restore (cr);
}
if (alpha_spec)
{
cairo_translate (cr, x, y);
cairo_scale (cr, width, height);
cairo_pattern_t *pattern = meta_alpha_gradient_spec_pattern (alpha_spec);
cairo_mask (cr, pattern);
cairo_pattern_destroy (pattern);
}
else
{
cairo_fill (cr);
}
cairo_restore (cr);
}
/* This code was originally rendering anti-aliased using X primitives, and
* now has been switched to draw anti-aliased using cairo. In general, the
@@ -3601,7 +3231,6 @@ meta_draw_op_draw_with_env (const MetaDrawOp *op,
GdkRGBA color;
cairo_save (cr);
gtk_style_context_save (style_gtk);
cairo_set_line_width (cr, 1.0);
@@ -3759,94 +3388,63 @@ meta_draw_op_draw_with_env (const MetaDrawOp *op,
case META_DRAW_TINT:
{
int rx, ry, rwidth, rheight;
gboolean needs_alpha;
needs_alpha = op->data.tint.alpha_spec &&
(op->data.tint.alpha_spec->n_alphas > 1 ||
op->data.tint.alpha_spec->alphas[0] != 0xff);
rx = parse_x_position_unchecked (op->data.tint.x, env);
ry = parse_y_position_unchecked (op->data.tint.y, env);
rwidth = parse_size_unchecked (op->data.tint.width, env);
rheight = parse_size_unchecked (op->data.tint.height, env);
if (!needs_alpha)
{
meta_color_spec_render (op->data.tint.color_spec,
style_gtk, &color);
gdk_cairo_set_source_rgba (cr, &color);
meta_color_spec_render (op->data.tint.color_spec,
style_gtk, &color);
cairo_rectangle (cr, rx, ry, rwidth, rheight);
cairo_fill (cr);
}
else
{
GdkPixbuf *pixbuf;
if (op->data.tint.alpha_spec &&
op->data.tint.alpha_spec->n_alphas == 1)
color.alpha = op->data.tint.alpha_spec->alphas[0];
pixbuf = draw_op_as_pixbuf (op, style_gtk, info,
rwidth, rheight);
gdk_cairo_set_source_rgba (cr, &color);
if (pixbuf)
{
gdk_cairo_set_source_pixbuf (cr, pixbuf, rx, ry);
cairo_paint (cr);
g_object_unref (G_OBJECT (pixbuf));
}
}
cairo_rectangle (cr, rx, ry, rwidth, rheight);
cairo_fill (cr);
}
break;
case META_DRAW_GRADIENT:
{
int rx, ry, rwidth, rheight;
GdkPixbuf *pixbuf;
rx = parse_x_position_unchecked (op->data.gradient.x, env);
ry = parse_y_position_unchecked (op->data.gradient.y, env);
rwidth = parse_size_unchecked (op->data.gradient.width, env);
rheight = parse_size_unchecked (op->data.gradient.height, env);
pixbuf = draw_op_as_pixbuf (op, style_gtk, info,
rwidth, rheight);
if (pixbuf)
{
gdk_cairo_set_source_pixbuf (cr, pixbuf, rx, ry);
cairo_paint (cr);
g_object_unref (G_OBJECT (pixbuf));
}
meta_gradient_spec_render (op->data.gradient.gradient_spec,
op->data.gradient.alpha_spec,
cr, style_gtk,
rx, ry, rwidth, rheight);
}
break;
case META_DRAW_IMAGE:
{
int rx, ry, rwidth, rheight;
GdkPixbuf *pixbuf;
if (op->data.image.pixbuf)
{
env->object_width = gdk_pixbuf_get_width (op->data.image.pixbuf);
env->object_height = gdk_pixbuf_get_height (op->data.image.pixbuf);
}
if (op->data.image.pixbuf == NULL)
break;
env->object_width = gdk_pixbuf_get_width (op->data.image.pixbuf);
env->object_height = gdk_pixbuf_get_height (op->data.image.pixbuf);
rx = parse_x_position_unchecked (op->data.image.x, env);
ry = parse_y_position_unchecked (op->data.image.y, env);
rwidth = parse_size_unchecked (op->data.image.width, env);
rheight = parse_size_unchecked (op->data.image.height, env);
pixbuf = draw_op_as_pixbuf (op, style_gtk, info,
rwidth, rheight);
if (pixbuf)
{
rx = parse_x_position_unchecked (op->data.image.x, env);
ry = parse_y_position_unchecked (op->data.image.y, env);
gdk_cairo_set_source_pixbuf (cr, pixbuf, rx, ry);
cairo_paint (cr);
g_object_unref (G_OBJECT (pixbuf));
}
draw_image (cr,
op->data.image.pixbuf,
op->data.image.fill_type,
op->data.image.alpha_spec,
rx, ry, rwidth, rheight);
}
break;
@@ -3916,24 +3514,27 @@ meta_draw_op_draw_with_env (const MetaDrawOp *op,
case META_DRAW_ICON:
{
int rx, ry, rwidth, rheight;
GdkPixbuf *pixbuf;
GdkPixbuf *src;
rwidth = parse_size_unchecked (op->data.icon.width, env);
rheight = parse_size_unchecked (op->data.icon.height, env);
pixbuf = draw_op_as_pixbuf (op, style_gtk, info,
rwidth, rheight);
if (info->mini_icon &&
rwidth < gdk_pixbuf_get_width (info->mini_icon) &&
rheight < gdk_pixbuf_get_height (info->mini_icon))
src = info->mini_icon;
else if (info->icon)
src = info->icon;
else
break;
if (pixbuf)
{
rx = parse_x_position_unchecked (op->data.icon.x, env);
ry = parse_y_position_unchecked (op->data.icon.y, env);
rx = parse_x_position_unchecked (op->data.icon.x, env);
ry = parse_y_position_unchecked (op->data.icon.y, env);
gdk_cairo_set_source_pixbuf (cr, pixbuf, rx, ry);
cairo_paint (cr);
g_object_unref (G_OBJECT (pixbuf));
}
draw_image (cr, src,
op->data.icon.fill_type,
op->data.icon.alpha_spec,
rx, ry, rwidth, rheight);
}
break;
@@ -4053,7 +3654,6 @@ meta_draw_op_draw_with_env (const MetaDrawOp *op,
}
cairo_restore (cr);
gtk_style_context_restore (style_gtk);
}
void

View File

@@ -75,6 +75,14 @@ compute_scale (MetaOutput *output)
output->crtc->rect.width >= SMALLEST_4K_WIDTH)
goto out;
/* Somebody encoded the aspect ratio (16/9 or 16/10)
* instead of the physical size */
if ((output->width_mm == 160 && output->height_mm == 90) ||
(output->width_mm == 160 && output->height_mm == 100) ||
(output->width_mm == 16 && output->height_mm == 9) ||
(output->width_mm == 16 && output->height_mm == 10))
goto out;
if (output->width_mm > 0 && output->height_mm > 0)
{
double dpi_x, dpi_y;

View File

@@ -518,10 +518,11 @@ meta_wayland_pointer_set_focus (MetaWaylandPointer *pointer,
clutter_input_device_get_coords (pointer->device, NULL, &pos);
meta_window_handle_enter (pointer->focus_surface->window,
/* XXX -- can we reliably get a timestamp for setting focus? */
clutter_get_current_event_time (),
pos.x, pos.y);
if (pointer->focus_surface->window)
meta_window_handle_enter (pointer->focus_surface->window,
/* XXX -- can we reliably get a timestamp for setting focus? */
clutter_get_current_event_time (),
pos.x, pos.y);
move_resources_for_client (&pointer->focus_resource_list,
&pointer->resource_list,

View File

@@ -32,6 +32,7 @@
#include <wayland-server.h>
#include "gtk-shell-server-protocol.h"
#include "xdg-shell-server-protocol.h"
#include "scaler-server-protocol.h"
#include "meta-wayland-private.h"
#include "meta-xwayland-private.h"
@@ -265,6 +266,7 @@ pending_state_init (MetaWaylandPendingState *state)
wl_list_init (&state->frame_callback_list);
state->has_new_geometry = FALSE;
state->viewport.changed = FALSE;
}
static void
@@ -420,6 +422,12 @@ commit_pending_state (MetaWaylandSurface *surface,
wl_list_insert_list (&compositor->frame_callbacks, &pending->frame_callback_list);
wl_list_init (&pending->frame_callback_list);
if (pending->viewport.changed)
meta_surface_actor_set_viewport (surface->surface_actor,
&pending->viewport.src_rect,
pending->viewport.dest_width,
pending->viewport.dest_height);
if (surface == compositor->seat->pointer.cursor_surface)
cursor_surface_commit (surface, pending);
else if (meta_wayland_data_device_is_dnd_surface (&compositor->seat->data_device, surface))
@@ -1681,6 +1689,136 @@ bind_subcompositor (struct wl_client *client,
wl_resource_set_implementation (resource, &meta_wayland_subcompositor_interface, data, NULL);
}
static void
destroy_wl_viewport (struct wl_resource *resource)
{
MetaWaylandSurface *surface = wl_resource_get_user_data (resource);
surface->has_viewport = FALSE;
surface->pending.viewport.changed = TRUE;
}
static void
wl_viewport_destroy (struct wl_client *client,
struct wl_resource *resource)
{
wl_resource_destroy (resource);
}
static void
viewport_set_src (struct wl_resource *resource,
wl_fixed_t src_x,
wl_fixed_t src_y,
wl_fixed_t src_width,
wl_fixed_t src_height)
{
MetaWaylandSurface *surface = wl_resource_get_user_data (resource);
surface->pending.viewport.src_rect.x = wl_fixed_to_int (src_x);
surface->pending.viewport.src_rect.y = wl_fixed_to_int (src_y);
surface->pending.viewport.src_rect.width = wl_fixed_to_int (src_width);
surface->pending.viewport.src_rect.height = wl_fixed_to_int (src_height);
surface->pending.viewport.changed = TRUE;
}
static void
viewport_set_dest (struct wl_resource *resource,
int dst_width,
int dst_height)
{
MetaWaylandSurface *surface = wl_resource_get_user_data (resource);
surface->pending.viewport.dest_width = dst_width;
surface->pending.viewport.dest_height = dst_height;
surface->pending.viewport.changed = TRUE;
}
static void
wl_viewport_set (struct wl_client *client,
struct wl_resource *resource,
wl_fixed_t src_x,
wl_fixed_t src_y,
wl_fixed_t src_width,
wl_fixed_t src_height,
int dst_width,
int dst_height)
{
viewport_set_src (resource, src_x, src_y, src_width, src_height);
viewport_set_dest (resource, dst_width, dst_height);
}
static void
wl_viewport_set_source (struct wl_client *client,
struct wl_resource *resource,
wl_fixed_t src_x,
wl_fixed_t src_y,
wl_fixed_t src_width,
wl_fixed_t src_height)
{
viewport_set_src (resource, src_x, src_y, src_width, src_height);
}
static void
wl_viewport_set_destination (struct wl_client *client,
struct wl_resource *resource,
int dst_width,
int dst_height)
{
viewport_set_dest (resource, dst_width, dst_height);
}
static const struct wl_viewport_interface meta_wayland_viewport_interface = {
wl_viewport_destroy,
wl_viewport_set,
wl_viewport_set_source,
wl_viewport_set_destination,
};
static void
wl_scaler_destroy (struct wl_client *client,
struct wl_resource *resource)
{
wl_resource_destroy (resource);
}
static void
wl_scaler_get_viewport (struct wl_client *client,
struct wl_resource *master_resource,
uint32_t viewport_id,
struct wl_resource *surface_resource)
{
struct wl_resource *resource;
MetaWaylandSurface *surface = wl_resource_get_user_data (surface_resource);
if (surface->has_viewport)
{
wl_resource_post_error (master_resource,
WL_SCALER_ERROR_VIEWPORT_EXISTS,
"viewport already exists on surface");
return;
}
resource = wl_resource_create (client, &wl_viewport_interface, wl_resource_get_version (master_resource), viewport_id);
wl_resource_set_implementation (resource, &meta_wayland_viewport_interface, surface, destroy_wl_viewport);
surface->has_viewport = TRUE;
}
static const struct wl_scaler_interface meta_wayland_scaler_interface = {
wl_scaler_destroy,
wl_scaler_get_viewport,
};
static void
bind_scaler (struct wl_client *client,
void *data,
guint32 version,
guint32 id)
{
struct wl_resource *resource;
resource = wl_resource_create (client, &wl_scaler_interface, version, id);
wl_resource_set_implementation (resource, &meta_wayland_scaler_interface, data, NULL);
}
void
meta_wayland_shell_init (MetaWaylandCompositor *compositor)
{
@@ -1707,6 +1845,12 @@ meta_wayland_shell_init (MetaWaylandCompositor *compositor)
META_WL_SUBCOMPOSITOR_VERSION,
compositor, bind_subcompositor) == NULL)
g_error ("Failed to register a global wl-subcompositor object");
if (wl_global_create (compositor->wayland_display,
&wl_scaler_interface,
META_WL_SCALER_VERSION,
compositor, bind_scaler) == NULL)
g_error ("Failed to register a global wl-subcompositor object");
}
static void

View File

@@ -58,6 +58,12 @@ typedef struct
MetaRectangle new_geometry;
gboolean has_new_geometry;
struct {
gboolean changed;
cairo_rectangle_int_t src_rect;
int32_t dest_width, dest_height;
} viewport;
} MetaWaylandPendingState;
struct _MetaWaylandSurface
@@ -72,6 +78,7 @@ struct _MetaWaylandSurface
int scale;
int32_t offset_x, offset_y;
GList *subsurfaces;
gboolean has_viewport;
/* All the pending state that wl_surface.commit will apply. */
MetaWaylandPendingState pending;

View File

@@ -44,5 +44,6 @@
#define META_XSERVER_VERSION 1
#define META_GTK_SHELL_VERSION 1
#define META_WL_SUBCOMPOSITOR_VERSION 1
#define META_WL_SCALER_VERSION 2
#endif

View File

@@ -0,0 +1,210 @@
<?xml version="1.0" encoding="UTF-8"?>
<protocol name="scaler">
<copyright>
Copyright © 2013-2014 Collabora, Ltd.
Permission to use, copy, modify, distribute, and sell this
software and its documentation for any purpose is hereby granted
without fee, provided that the above copyright notice appear in
all copies and that both that copyright notice and this permission
notice appear in supporting documentation, and that the name of
the copyright holders not be used in advertising or publicity
pertaining to distribution of the software without specific,
written prior permission. The copyright holders make no
representations about the suitability of this software for any
purpose. It is provided "as is" without express or implied
warranty.
THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS
SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
FITNESS, IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY
SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN
AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,
ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF
THIS SOFTWARE.
</copyright>
<interface name="wl_scaler" version="2">
<description summary="surface cropping and scaling">
The global interface exposing surface cropping and scaling
capabilities is used to instantiate an interface extension for a
wl_surface object. This extended interface will then allow
cropping and scaling the surface contents, effectively
disconnecting the direct relationship between the buffer and the
surface size.
</description>
<request name="destroy" type="destructor">
<description summary="unbind from the cropping and scaling interface">
Informs the server that the client will not be using this
protocol object anymore. This does not affect any other objects,
wl_viewport objects included.
</description>
</request>
<enum name="error">
<entry name="viewport_exists" value="0"
summary="the surface already has a viewport object associated"/>
</enum>
<request name="get_viewport">
<description summary="extend surface interface for crop and scale">
Instantiate an interface extension for the given wl_surface to
crop and scale its content. If the given wl_surface already has
a wl_viewport object associated, the viewport_exists
protocol error is raised.
</description>
<arg name="id" type="new_id" interface="wl_viewport"
summary="the new viewport interface id"/>
<arg name="surface" type="object" interface="wl_surface"
summary="the surface"/>
</request>
</interface>
<interface name="wl_viewport" version="2">
<description summary="crop and scale interface to a wl_surface">
An additional interface to a wl_surface object, which allows the
client to specify the cropping and scaling of the surface
contents.
This interface allows to define the source rectangle (src_x,
src_y, src_width, src_height) from where to take the wl_buffer
contents, and scale that to destination size (dst_width,
dst_height). This state is double-buffered, and is applied on the
next wl_surface.commit.
The two parts of crop and scale state are independent: the source
rectangle, and the destination size. Initially both are unset, that
is, no scaling is applied. The whole of the current wl_buffer is
used as the source, and the surface size is as defined in
wl_surface.attach.
If the destination size is set, it causes the surface size to become
dst_width, dst_height. The source (rectangle) is scaled to exactly
this size. This overrides whatever the attached wl_buffer size is,
unless the wl_buffer is NULL. If the wl_buffer is NULL, the surface
has no content and therefore no size. Otherwise, the size is always
at least 1x1 in surface coordinates.
If the source rectangle is set, it defines what area of the
wl_buffer is taken as the source. If the source rectangle is set and
the destination size is not set, the surface size becomes the source
rectangle size rounded up to the nearest integer. If the source size
is already exactly integers, this results in cropping without scaling.
The coordinate transformations from buffer pixel coordinates up to
the surface-local coordinates happen in the following order:
1. buffer_transform (wl_surface.set_buffer_transform)
2. buffer_scale (wl_surface.set_buffer_scale)
3. crop and scale (wl_viewport.set*)
This means, that the source rectangle coordinates of crop and scale
are given in the coordinates after the buffer transform and scale,
i.e. in the coordinates that would be the surface-local coordinates
if the crop and scale was not applied.
If the source rectangle is partially or completely outside of the
wl_buffer, then the surface contents are undefined (not void), and
the surface size is still dst_width, dst_height.
The x, y arguments of wl_surface.attach are applied as normal to
the surface. They indicate how many pixels to remove from the
surface size from the left and the top. In other words, they are
still in the surface-local coordinate system, just like dst_width
and dst_height are.
If the wl_surface associated with the wl_viewport is destroyed,
the wl_viewport object becomes inert.
If the wl_viewport object is destroyed, the crop and scale
state is removed from the wl_surface. The change will be applied
on the next wl_surface.commit.
</description>
<request name="destroy" type="destructor">
<description summary="remove scaling and cropping from the surface">
The associated wl_surface's crop and scale state is removed.
The change is applied on the next wl_surface.commit.
</description>
</request>
<enum name="error">
<entry name="bad_value" value="0"
summary="negative or zero values in width or height"/>
</enum>
<request name="set">
<description summary="set the crop and scale state">
Set both source rectangle and destination size of the associated
wl_surface. See wl_viewport for the description, and relation to
the wl_buffer size.
The bad_value protocol error is raised if src_width or
src_height is negative, or if dst_width or dst_height is not
positive.
The crop and scale state is double-buffered state, and will be
applied on the next wl_surface.commit.
Arguments dst_x and dst_y do not exist here, use the x and y
arguments to wl_surface.attach. The x, y, dst_width, and dst_height
define the surface-local coordinate system irrespective of the
attached wl_buffer size.
</description>
<arg name="src_x" type="fixed" summary="source rectangle x"/>
<arg name="src_y" type="fixed" summary="source rectangle y"/>
<arg name="src_width" type="fixed" summary="source rectangle width"/>
<arg name="src_height" type="fixed" summary="source rectangle height"/>
<arg name="dst_width" type="int" summary="surface width"/>
<arg name="dst_height" type="int" summary="surface height"/>
</request>
<request name="set_source" since="2">
<description summary="set the source rectangle for cropping">
Set the source rectangle of the associated wl_surface. See
wl_viewport for the description, and relation to the wl_buffer
size.
If width is -1.0 and height is -1.0, the destination size is unset
instead. Any other pair of values for width and height that
contains zero or negative values raises the bad_value protocol
error.
The crop and scale state is double-buffered state, and will be
applied on the next wl_surface.commit.
</description>
<arg name="x" type="fixed" summary="source rectangle x"/>
<arg name="y" type="fixed" summary="source rectangle y"/>
<arg name="width" type="fixed" summary="source rectangle width"/>
<arg name="height" type="fixed" summary="source rectangle height"/>
</request>
<request name="set_destination" since="2">
<description summary="set the surface size for scaling">
Set the destination size of the associated wl_surface. See
wl_viewport for the description, and relation to the wl_buffer
size.
If width is -1 and height is -1, the destination size is unset
instead. Any other pair of values for width and height that
contains zero or negative values raises the bad_value protocol
error.
The crop and scale state is double-buffered state, and will be
applied on the next wl_surface.commit.
Arguments x and y do not exist here, use the x and y arguments to
wl_surface.attach. The x, y, width, and height define the
surface-local coordinate system irrespective of the attached
wl_buffer size.
</description>
<arg name="width" type="int" summary="surface width"/>
<arg name="height" type="int" summary="surface height"/>
</request>
</interface>
</protocol>