summaryrefslogtreecommitdiff
path: root/gnome/window-decorator
diff options
context:
space:
mode:
Diffstat (limited to 'gnome/window-decorator')
-rw-r--r--gnome/window-decorator/Makefile.am9
-rw-r--r--gnome/window-decorator/TODO6
-rw-r--r--gnome/window-decorator/gnome-window-decorator.c3163
3 files changed, 3178 insertions, 0 deletions
diff --git a/gnome/window-decorator/Makefile.am b/gnome/window-decorator/Makefile.am
new file mode 100644
index 00000000..9702084a
--- /dev/null
+++ b/gnome/window-decorator/Makefile.am
@@ -0,0 +1,9 @@
+if USE_GNOME
+gnome_window_decorator_LDADD = @GNOME_WINDOW_DECORATOR_LIBS@
+gnome_window_decorator_SOURCES = gnome-window-decorator.c
+gnome_window_decorator_program = gnome-window-decorator
+endif
+
+INCLUDES = @GNOME_WINDOW_DECORATOR_CFLAGS@
+
+bin_PROGRAMS = $(gnome_window_decorator_program) \ No newline at end of file
diff --git a/gnome/window-decorator/TODO b/gnome/window-decorator/TODO
new file mode 100644
index 00000000..d1b8e931
--- /dev/null
+++ b/gnome/window-decorator/TODO
@@ -0,0 +1,6 @@
+
+* Plugin interface
+
+* Plugin with SVG-based theme support
+
+* Plugin that supports old metacity themes \ No newline at end of file
diff --git a/gnome/window-decorator/gnome-window-decorator.c b/gnome/window-decorator/gnome-window-decorator.c
new file mode 100644
index 00000000..871c75a1
--- /dev/null
+++ b/gnome/window-decorator/gnome-window-decorator.c
@@ -0,0 +1,3163 @@
+/*
+ * Copyright © 2006 Novell, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#include <X11/Xlib.h>
+#include <X11/Xatom.h>
+#include <X11/cursorfont.h>
+
+#ifndef GTK_DISABLE_DEPRECATED
+#define GTK_DISABLE_DEPRECATED
+#endif
+
+#include <gtk/gtk.h>
+#include <gtk/gtkwindow.h>
+#include <gdk/gdkx.h>
+
+#define WNCK_I_KNOW_THIS_IS_UNSTABLE
+#include <libwnck/libwnck.h>
+#include <libwnck/window-action-menu.h>
+
+#include <cairo.h>
+#include <cairo-xlib.h>
+
+#if CAIRO_VERSION < CAIRO_VERSION_ENCODE(1, 1, 0)
+#define CAIRO_EXTEND_PAD CAIRO_EXTEND_NONE
+#endif
+
+#include <pango/pango-context.h>
+#include <pango/pangocairo.h>
+
+#include <string.h>
+#include <stdlib.h>
+#include <math.h>
+#include <limits.h>
+
+#define LEFT_SPACE 12
+#define RIGHT_SPACE 14
+#define TOP_SPACE 27
+#define BOTTOM_SPACE 14
+
+#define ICON_SPACE 20
+#define BUTTON_SPACE 52
+
+typedef struct _extents {
+ gint left;
+ gint right;
+ gint top;
+ gint bottom;
+} extents;
+
+#define GRAVITY_WEST (0)
+#define GRAVITY_EAST (1 << 0)
+#define GRAVITY_NORTH (0)
+#define GRAVITY_SOUTH (1 << 1)
+
+#define ALIGN_LEFT (0)
+#define ALIGN_RIGHT (1 << 0)
+#define ALIGN_TOP (0)
+#define ALIGN_BOTTOM (1 << 1)
+
+#define XX_MASK (1 << 6)
+#define XY_MASK (1 << 7)
+#define YX_MASK (1 << 8)
+#define YY_MASK (1 << 9)
+
+#define WM_MOVERESIZE_SIZE_TOPLEFT 0
+#define WM_MOVERESIZE_SIZE_TOP 1
+#define WM_MOVERESIZE_SIZE_TOPRIGHT 2
+#define WM_MOVERESIZE_SIZE_RIGHT 3
+#define WM_MOVERESIZE_SIZE_BOTTOMRIGHT 4
+#define WM_MOVERESIZE_SIZE_BOTTOM 5
+#define WM_MOVERESIZE_SIZE_BOTTOMLEFT 6
+#define WM_MOVERESIZE_SIZE_LEFT 7
+#define WM_MOVERESIZE_MOVE 8
+#define WM_MOVERESIZE_SIZE_KEYBOARD 9
+#define WM_MOVERESIZE_MOVE_KEYBOARD 10
+
+typedef struct _point {
+ gint x;
+ gint y;
+ gint gravity;
+} point;
+
+typedef struct _quad {
+ point p1;
+ point p2;
+ gint max_width;
+ gint max_height;
+ gint align;
+ cairo_matrix_t m;
+} quad;
+
+#ifdef __SUNPRO_C
+#pragma align 4 (_shadow)
+#endif
+#ifdef __GNUC__
+static const guint8 _shadow[] __attribute__ ((__aligned__ (4))) =
+#else
+ static const guint8 _shadow[] =
+#endif
+{ ""
+ /* Pixbuf magic (0x47646b50) */
+ "GdkP"
+ /* length: header (24) + pixel_data (400) */
+ "\0\0\1\250"
+ /* pixdata_type (0x1010002) */
+ "\1\1\0\2"
+ /* rowstride (40) */
+ "\0\0\0("
+ /* width (10) */
+ "\0\0\0\12"
+ /* height (10) */
+ "\0\0\0\12"
+ /* pixel_data: */
+ "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\1\0\0\0\2\0\0\0\2\0\0\0\1\0\0\0\0\0\0"
+ "\0\0\0\0\0\0\0\0\0\0\0\0\0\1\0\0\0\3\0\0\0\5\0\0\0\7\0\0\0\7\0\0\0\5"
+ "\0\0\0\3\0\0\0\1\0\0\0\0\0\0\0\1\0\0\0\3\0\0\0\7\0\0\0\15\0\0\0\22\0"
+ "\0\0\22\0\0\0\15\0\0\0\7\0\0\0\3\0\0\0\1\0\0\0\1\0\0\0\5\0\0\0\15\0\0"
+ "\0\30\0\0\0\40\0\0\0\40\0\0\0\30\0\0\0\15\0\0\0\5\0\0\0\1\0\0\0\2\0\0"
+ "\0\7\0\0\0\22\0\0\0\40\377\377\377\377\0\0\0+\0\0\0\40\0\0\0\22\0\0\0"
+ "\7\0\0\0\2\0\0\0\2\0\0\0\7\0\0\0\22\0\0\0\40\0\0\0+\0\0\0+\0\0\0\40\0"
+ "\0\0\22\0\0\0\7\0\0\0\2\0\0\0\1\0\0\0\5\0\0\0\15\0\0\0\30\0\0\0\40\0"
+ "\0\0\40\0\0\0\30\0\0\0\15\0\0\0\5\0\0\0\1\0\0\0\1\0\0\0\3\0\0\0\7\0\0"
+ "\0\15\0\0\0\22\0\0\0\22\0\0\0\15\0\0\0\7\0\0\0\3\0\0\0\1\0\0\0\0\0\0"
+ "\0\1\0\0\0\3\0\0\0\5\0\0\0\7\0\0\0\7\0\0\0\5\0\0\0\3\0\0\0\1\0\0\0\0"
+ "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\1\0\0\0\2\0\0\0\2\0\0\0\1\0\0\0\0\0\0"
+ "\0\0\0\0\0\0"
+};
+
+static extents _shadow_extents = { 0, 0, 0, 0 };
+
+static quad _shadow_quads[] = {
+ {
+ { -4, -4, GRAVITY_NORTH | GRAVITY_WEST },
+ { 0, 0, GRAVITY_NORTH | GRAVITY_WEST },
+ SHRT_MAX, SHRT_MAX,
+ 0,
+ {
+ 1.0, 0.0,
+ 0.0, 1.0,
+ 0.0, 0.0
+ }
+ }, {
+ { 0, -4, GRAVITY_NORTH | GRAVITY_WEST },
+ { 0, 0, GRAVITY_NORTH | GRAVITY_EAST },
+ SHRT_MAX, SHRT_MAX,
+ 0,
+ {
+ 0.0, 0.0,
+ 0.0, 1.0,
+ 4.0, 0.0
+ }
+ }, {
+ { 0, -4, GRAVITY_NORTH | GRAVITY_EAST },
+ { 5, 0, GRAVITY_NORTH | GRAVITY_EAST },
+ SHRT_MAX, SHRT_MAX,
+ 0,
+ {
+ 1.0, 0.0,
+ 0.0, 1.0,
+ 4.0, 0.0
+ }
+ },
+
+ {
+ { -4, 0, GRAVITY_NORTH | GRAVITY_WEST },
+ { 0, 0, GRAVITY_SOUTH | GRAVITY_WEST },
+ SHRT_MAX, SHRT_MAX,
+ 0,
+ {
+ 1.0, 0.0,
+ 0.0, 0.0,
+ 0.0, 4.0
+ }
+ }, {
+
+ { 0, 0, GRAVITY_NORTH | GRAVITY_EAST },
+ { 5, 0, GRAVITY_SOUTH | GRAVITY_EAST },
+ SHRT_MAX, SHRT_MAX,
+ 0,
+ {
+ 1.0, 0.0,
+ 0.0, 0.0,
+ 5.0, 4.0
+ }
+ },
+
+ {
+ { -4, 0, GRAVITY_SOUTH | GRAVITY_WEST },
+ { 0, 5, GRAVITY_SOUTH | GRAVITY_WEST },
+ SHRT_MAX, SHRT_MAX,
+ 0,
+ {
+ 1.0, 0.0,
+ 0.0, 1.0,
+ 0.0, 5.0
+ }
+ }, {
+ { 0, 0, GRAVITY_SOUTH | GRAVITY_WEST },
+ { 0, 5, GRAVITY_SOUTH | GRAVITY_EAST },
+ SHRT_MAX, SHRT_MAX,
+ 0,
+ {
+ 0.0, 0.0,
+ 0.0, 1.0,
+ 4.0, 5.0
+ }
+ }, {
+ { 0, 0, GRAVITY_SOUTH | GRAVITY_EAST },
+ { 5, 5, GRAVITY_SOUTH | GRAVITY_EAST },
+ SHRT_MAX, SHRT_MAX,
+ 0,
+ {
+ 1.0, 0.0,
+ 0.0, 1.0,
+ 5.0, 5.0
+ }
+ }
+};
+
+#ifdef __SUNPRO_C
+#pragma align 4 (_large_shadow)
+#endif
+#ifdef __GNUC__
+static const guint8 _large_shadow[] __attribute__ ((__aligned__ (4))) =
+#else
+ static const guint8 _large_shadow[] =
+#endif
+{ ""
+ /* Pixbuf magic (0x47646b50) */
+ "GdkP"
+ /* length: header (24) + pixel_data (2916) */
+ "\0\0\13|"
+ /* pixdata_type (0x1010002) */
+ "\1\1\0\2"
+ /* rowstride (108) */
+ "\0\0\0l"
+ /* width (27) */
+ "\0\0\0\33"
+ /* height (27) */
+ "\0\0\0\33"
+ /* pixel_data: */
+ "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"
+ "\0\0\0\0\0\1\0\0\0\1\0\0\0\1\0\0\0\1\0\0\0\1\0\0\0\1\0\0\0\1\0\0\0\1"
+ "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"
+ "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"
+ "\0\0\0\1\0\0\0\1\0\0\0\1\0\0\0\2\0\0\0\2\0\0\0\2\0\0\0\2\0\0\0\2\0\0"
+ "\0\2\0\0\0\2\0\0\0\1\0\0\0\1\0\0\0\1\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"
+ "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"
+ "\0\1\0\0\0\1\0\0\0\2\0\0\0\3\0\0\0\4\0\0\0\5\0\0\0\5\0\0\0\6\0\0\0\6"
+ "\0\0\0\6\0\0\0\5\0\0\0\4\0\0\0\4\0\0\0\2\0\0\0\2\0\0\0\1\0\0\0\1\0\0"
+ "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"
+ "\0\0\0\1\0\0\0\2\0\0\0\3\0\0\0\4\0\0\0\6\0\0\0\10\0\0\0\12\0\0\0\13\0"
+ "\0\0\15\0\0\0\15\0\0\0\14\0\0\0\13\0\0\0\11\0\0\0\7\0\0\0\5\0\0\0\4\0"
+ "\0\0\2\0\0\0\1\0\0\0\1\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"
+ "\0\0\0\0\0\0\0\0\1\0\0\0\2\0\0\0\3\0\0\0\5\0\0\0\10\0\0\0\13\0\0\0\16"
+ "\0\0\0\22\0\0\0\24\0\0\0\26\0\0\0\26\0\0\0\26\0\0\0\23\0\0\0\21\0\0\0"
+ "\15\0\0\0\12\0\0\0\7\0\0\0\5\0\0\0\2\0\0\0\1\0\0\0\1\0\0\0\0\0\0\0\0"
+ "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\1\0\0\0\2\0\0\0\3\0\0\0\6\0\0\0\11\0\0"
+ "\0\16\0\0\0\23\0\0\0\30\0\0\0\35\0\0\0\40\0\0\0#\0\0\0#\0\0\0\"\0\0\0"
+ "\37\0\0\0\33\0\0\0\26\0\0\0\21\0\0\0\14\0\0\0\10\0\0\0\5\0\0\0\2\0\0"
+ "\0\1\0\0\0\1\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\1\0\0\0\1\0\0\0\3\0\0\0\5"
+ "\0\0\0\12\0\0\0\17\0\0\0\26\0\0\0\35\0\0\0$\0\0\0+\0\0\0""0\0\0\0""4"
+ "\0\0\0""4\0\0\0""2\0\0\0.\0\0\0)\0\0\0\"\0\0\0\32\0\0\0\23\0\0\0\15\0"
+ "\0\0\10\0\0\0\5\0\0\0\2\0\0\0\1\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\1\0\0\0"
+ "\2\0\0\0\4\0\0\0\10\0\0\0\16\0\0\0\26\0\0\0\37\0\0\0(\0\0\0""2\0\0\0"
+ ";\0\0\0A\0\0\0F\0\0\0G\0\0\0D\0\0\0@\0\0\0""8\0\0\0/\0\0\0%\0\0\0\34"
+ "\0\0\0\23\0\0\0\14\0\0\0\7\0\0\0\4\0\0\0\2\0\0\0\1\0\0\0\0\0\0\0\1\0"
+ "\0\0\1\0\0\0\3\0\0\0\7\0\0\0\13\0\0\0\23\0\0\0\35\0\0\0)\0\0\0""5\0\0"
+ "\0A\0\0\0L\0\0\0S\0\0\0X\0\0\0Y\0\0\0W\0\0\0Q\0\0\0I\0\0\0=\0\0\0""1"
+ "\0\0\0%\0\0\0\32\0\0\0\21\0\0\0\12\0\0\0\5\0\0\0\2\0\0\0\1\0\0\0\0\0"
+ "\0\0\1\0\0\0\2\0\0\0\4\0\0\0\10\0\0\0\17\0\0\0\31\0\0\0%\0\0\0""3\0\0"
+ "\0A\0\0\0P\0\0\0[\0\0\0d\0\0\0i\0\0\0k\0\0\0h\0\0\0b\0\0\0X\0\0\0L\0"
+ "\0\0=\0\0\0/\0\0\0\"\0\0\0\26\0\0\0\15\0\0\0\7\0\0\0\4\0\0\0\2\0\0\0"
+ "\1\0\0\0\1\0\0\0\2\0\0\0\5\0\0\0\13\0\0\0\23\0\0\0\36\0\0\0,\0\0\0=\0"
+ "\0\0M\0\0\0\\\0\0\0i\0\0\0r\0\0\0w\0\0\0y\0\0\0v\0\0\0p\0\0\0e\0\0\0"
+ "X\0\0\0I\0\0\0""8\0\0\0)\0\0\0\33\0\0\0\21\0\0\0\12\0\0\0\5\0\0\0\2\0"
+ "\0\0\1\0\0\0\1\0\0\0\3\0\0\0\7\0\0\0\15\0\0\0\26\0\0\0#\0\0\0""3\0\0"
+ "\0D\0\0\0V\0\0\0f\0\0\0s\0\0\0|\0\0\0\202\0\0\0\203\0\0\0\200\0\0\0z"
+ "\0\0\0p\0\0\0b\0\0\0R\0\0\0@\0\0\0/\0\0\0\40\0\0\0\24\0\0\0\13\0\0\0"
+ "\6\0\0\0\2\0\0\0\1\0\0\0\1\0\0\0\4\0\0\0\7\0\0\0\16\0\0\0\31\0\0\0'\0"
+ "\0\0""8\0\0\0K\0\0\0^\0\0\0n\0\0\0{\0\0\0\204\0\0\0\211\0\0\0\213\0\0"
+ "\0\210\0\0\0\202\0\0\0x\0\0\0k\0\0\0Z\0\0\0G\0\0\0""5\0\0\0%\0\0\0\27"
+ "\0\0\0\15\0\0\0\7\0\0\0\3\0\0\0\1\0\0\0\1\0\0\0\4\0\0\0\10\0\0\0\20\0"
+ "\0\0\33\0\0\0*\0\0\0<\0\0\0O\0\0\0b\0\0\0s\0\0\0\200\0\0\0\211\0\0\0"
+ "\216\0\0\0\217\0\0\0\215\0\0\0\210\0\0\0~\0\0\0p\0\0\0_\0\0\0M\0\0\0"
+ "9\0\0\0(\0\0\0\31\0\0\0\16\0\0\0\10\0\0\0\4\0\0\0\1\0\0\0\1\0\0\0\3\0"
+ "\0\0\7\0\0\0\16\0\0\0\27\0\0\0%\0\0\0""6\0\0\0I\0\0\0[\0\0\0k\0\0\0y"
+ "\0\0\0\202\0\0\0\210\0\0\0\211\0\0\0\210\0\0\0\202\0\0\0y\0\0\0k\0\0"
+ "\0[\0\0\0I\0\0\0""6\0\0\0%\0\0\0\27\0\0\0\16\0\0\0\7\0\0\0\3\0\0\0\1"
+ "\0\0\0\1\0\0\0\2\0\0\0\6\0\0\0\14\0\0\0\25\0\0\0!\0\0\0""1\0\0\0A\0\0"
+ "\0S\0\0\0c\0\0\0p\0\0\0z\0\0\0\200\0\0\0\202\0\0\0\200\0\0\0z\0\0\0p"
+ "\0\0\0c\0\0\0S\0\0\0A\0\0\0""0\0\0\0!\0\0\0\24\0\0\0\14\0\0\0\6\0\0\0"
+ "\3\0\0\0\1\0\0\0\1\0\0\0\2\0\0\0\5\0\0\0\12\0\0\0\21\0\0\0\34\0\0\0)"
+ "\0\0\0""9\0\0\0I\0\0\0X\0\0\0e\0\0\0n\0\0\0t\0\0\0v\0\0\0t\0\0\0n\0\0"
+ "\0e\0\0\0X\0\0\0I\0\0\0""9\0\0\0)\0\0\0\34\0\0\0\21\0\0\0\12\0\0\0\5"
+ "\0\0\0\2\0\0\0\1\0\0\0\1\0\0\0\2\0\0\0\4\0\0\0\10\0\0\0\16\0\0\0\27\0"
+ "\0\0\"\0\0\0/\0\0\0=\0\0\0K\0\0\0W\0\0\0`\0\0\0e\0\0\0h\0\0\0e\0\0\0"
+ "`\0\0\0W\0\0\0K\0\0\0=\0\0\0/\0\0\0\"\0\0\0\27\0\0\0\16\0\0\0\10\0\0"
+ "\0\4\0\0\0\2\0\0\0\1\0\0\0\0\0\0\0\1\0\0\0\2\0\0\0\5\0\0\0\12\0\0\0\21"
+ "\0\0\0\32\0\0\0%\0\0\0""1\0\0\0=\0\0\0G\0\0\0O\0\0\0U\0\0\0V\0\0\0U\0"
+ "\0\0O\0\0\0G\0\0\0=\0\0\0""1\0\0\0%\0\0\0\32\0\0\0\21\0\0\0\12\0\0\0"
+ "\5\0\0\0\2\0\0\0\1\0\0\0\0\0\0\0\0\0\0\0\1\0\0\0\2\0\0\0\4\0\0\0\7\0"
+ "\0\0\14\0\0\0\23\0\0\0\34\0\0\0%\0\0\0.\0\0\0""7\0\0\0>\0\0\0B\0\0\0"
+ "C\0\0\0B\0\0\0>\0\0\0""7\0\0\0.\0\0\0%\0\0\0\34\0\0\0\23\0\0\0\14\0\0"
+ "\0\7\0\0\0\4\0\0\0\2\0\0\0\1\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\1\0\0\0\2"
+ "\0\0\0\5\0\0\0\10\0\0\0\15\0\0\0\23\0\0\0\32\0\0\0!\0\0\0(\0\0\0,\0\0"
+ "\0""0\0\0\0""1\0\0\0""0\0\0\0,\0\0\0(\0\0\0!\0\0\0\32\0\0\0\23\0\0\0"
+ "\15\0\0\0\10\0\0\0\5\0\0\0\2\0\0\0\1\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"
+ "\0\0\0\1\0\0\0\1\0\0\0\2\0\0\0\5\0\0\0\10\0\0\0\14\0\0\0\20\0\0\0\26"
+ "\0\0\0\32\0\0\0\36\0\0\0\40\0\0\0!\0\0\0\40\0\0\0\36\0\0\0\32\0\0\0\26"
+ "\0\0\0\20\0\0\0\14\0\0\0\10\0\0\0\5\0\0\0\2\0\0\0\1\0\0\0\1\0\0\0\0\0"
+ "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\1\0\0\0\1\0\0\0\2\0\0\0\4\0\0\0"
+ "\7\0\0\0\12\0\0\0\15\0\0\0\20\0\0\0\23\0\0\0\24\0\0\0\24\0\0\0\24\0\0"
+ "\0\23\0\0\0\20\0\0\0\15\0\0\0\12\0\0\0\7\0\0\0\4\0\0\0\2\0\0\0\1\0\0"
+ "\0\1\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\1"
+ "\0\0\0\1\0\0\0\2\0\0\0\4\0\0\0\5\0\0\0\7\0\0\0\10\0\0\0\12\0\0\0\13\0"
+ "\0\0\13\0\0\0\13\0\0\0\12\0\0\0\10\0\0\0\7\0\0\0\5\0\0\0\4\0\0\0\2\0"
+ "\0\0\1\0\0\0\1\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"
+ "\0\0\0\0\0\0\0\0\0\0\0\0\1\0\0\0\1\0\0\0\2\0\0\0\2\0\0\0\4\0\0\0\4\0"
+ "\0\0\5\0\0\0\5\0\0\0\6\0\0\0\5\0\0\0\5\0\0\0\4\0\0\0\4\0\0\0\2\0\0\0"
+ "\2\0\0\0\1\0\0\0\1\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"
+ "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\1\0\0\0\1\0\0\0"
+ "\1\0\0\0\2\0\0\0\2\0\0\0\2\0\0\0\2\0\0\0\2\0\0\0\2\0\0\0\2\0\0\0\1\0"
+ "\0\0\1\0\0\0\1\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"
+ "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"
+ "\0\0\0\0\0\0\0\0\0\0\1\0\0\0\1\0\0\0\1\0\0\0\1\0\0\0\1\0\0\0\1\0\0\0"
+ "\1\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"
+ "\0\0\0\0\0\0\0"
+};
+
+static extents _win_extents = { 6, 6, 21, 6 };
+
+static quad _win_quads[] = {
+ {
+ { -LEFT_SPACE, -TOP_SPACE, GRAVITY_NORTH | GRAVITY_WEST },
+ { 0, 0, GRAVITY_NORTH | GRAVITY_WEST },
+ SHRT_MAX, SHRT_MAX,
+ 0,
+ {
+ 1.0, 0.0,
+ 0.0, 1.0,
+ 0.0, 0.0
+ }
+ }, {
+ { 0, -TOP_SPACE, GRAVITY_NORTH | GRAVITY_WEST },
+ { 0, 0, GRAVITY_NORTH | GRAVITY_EAST },
+ SHRT_MAX, SHRT_MAX,
+ ALIGN_LEFT,
+ {
+ 0.0, 0.0,
+ 0.0, 1.0,
+ LEFT_SPACE + 1, 0.0
+ }
+ }, {
+ { 0, -TOP_SPACE, GRAVITY_NORTH | GRAVITY_EAST },
+ { RIGHT_SPACE, 0, GRAVITY_NORTH | GRAVITY_EAST },
+ SHRT_MAX, SHRT_MAX,
+ 0,
+ {
+ 1.0, 0.0,
+ 0.0, 1.0,
+ LEFT_SPACE + 1.0, 0.0
+ }
+ },
+
+ {
+ { -LEFT_SPACE, 0, GRAVITY_NORTH | GRAVITY_WEST },
+ { 0, 0, GRAVITY_SOUTH | GRAVITY_WEST },
+ SHRT_MAX, SHRT_MAX,
+ 0,
+ {
+ 1.0, 0.0,
+ 0.0, 0.0,
+ 0.0, TOP_SPACE + 1.0
+ }
+ }, {
+
+ { 0, 0, GRAVITY_NORTH | GRAVITY_EAST },
+ { RIGHT_SPACE, 0, GRAVITY_SOUTH | GRAVITY_EAST },
+ SHRT_MAX, SHRT_MAX,
+ 0,
+ {
+ 1.0, 0.0,
+ 0.0, 0.0,
+ LEFT_SPACE + 1.0, TOP_SPACE + 1.0
+ }
+ },
+
+ {
+ { -LEFT_SPACE, 0, GRAVITY_SOUTH | GRAVITY_WEST },
+ { 0, 16, GRAVITY_SOUTH | GRAVITY_WEST },
+ SHRT_MAX, SHRT_MAX,
+ 0,
+ {
+ 1.0, 0.0,
+ 0.0, 1.0,
+ 0.0, TOP_SPACE + 1
+ }
+ }, {
+ { 0, 0, GRAVITY_SOUTH | GRAVITY_WEST },
+ { 0, BOTTOM_SPACE, GRAVITY_SOUTH | GRAVITY_EAST },
+ SHRT_MAX, SHRT_MAX,
+ 0,
+ {
+ 0.0, 0.0,
+ 0.0, 1.0,
+ LEFT_SPACE, TOP_SPACE + 1
+ }
+ }, {
+ { 0, 0, GRAVITY_SOUTH | GRAVITY_EAST },
+ { RIGHT_SPACE, BOTTOM_SPACE, GRAVITY_SOUTH | GRAVITY_EAST },
+ SHRT_MAX, SHRT_MAX,
+ 0,
+ {
+ 1.0, 0.0,
+ 0.0, 1.0,
+ LEFT_SPACE + 1, TOP_SPACE + 1
+ }
+ }
+};
+
+static quad _win_button_quads[] = {
+ {
+ { 0 /* + title width */, -TOP_SPACE, GRAVITY_NORTH | GRAVITY_WEST },
+ { -BUTTON_SPACE, 0, GRAVITY_NORTH | GRAVITY_EAST },
+ SHRT_MAX, SHRT_MAX,
+ 0,
+ {
+ 0.0, 0.0,
+ 0.0, 1.0,
+ 0.0 /* title width */, 0.0
+ }
+ }, {
+ { 0 /* title width + 1 */, -TOP_SPACE, GRAVITY_NORTH | GRAVITY_WEST },
+ { 0, 0, GRAVITY_NORTH | GRAVITY_EAST },
+ BUTTON_SPACE, SHRT_MAX,
+ ALIGN_RIGHT,
+ {
+ 1.0, 0.0,
+ 0.0, 1.0,
+ 0.0 /* title width + 1.0 */, 0.0
+ }
+ }
+};
+
+static GdkPixmap *shadow_pixmap = NULL;
+static GdkPixmap *large_shadow_pixmap = NULL;
+static GdkPixmap *decor_normal_pixmap = NULL;
+static GdkPixmap *decor_active_pixmap = NULL;
+
+static cairo_pattern_t *shadow_pattern;
+
+static Atom frame_window_atom;
+static Atom win_decor_atom;
+static Atom win_decor_sync_atom;
+static Atom wm_move_resize_atom;
+
+#define C(name) { 0, XC_ ## name }
+
+static struct _cursor {
+ Cursor cursor;
+ unsigned int shape;
+} cursor[3][3] = {
+ { C (top_left_corner), C (top_side), C (top_right_corner) },
+ { C (left_side), C (left_ptr), C (right_side) },
+ { C (bottom_left_corner), C (bottom_side), C (bottom_right_corner) }
+};
+
+static struct _pos {
+ int x, y, w, h;
+ int xw, yh, ww, hh;
+} pos[3][3] = {
+ {
+ { 0, 0, 10, 21, 0, 0, 0, 0 },
+ { 10, 0, -8, 6, 0, 0, 1, 0 },
+ { 2, 0, 10, 21, 1, 0, 0, 0 }
+ }, {
+ { 0, 10, 6, 11, 0, 0, 0, 1 },
+ { 6, 6, 0, 15, 0, 0, 1, 0 },
+ { 6, 10, 6, 11, 1, 0, 0, 1 }
+ }, {
+ { 0, 17, 10, 10, 0, 1, 0, 0 },
+ { 10, 21, -8, 6, 0, 1, 1, 0 },
+ { 2, 17, 10, 10, 1, 1, 0, 0 }
+ }
+}, bpos[3] = {
+ { -10, 6, 16, 16, 1, 0, 0, 0 },
+ { -26, 6, 16, 16, 1, 0, 0, 0 },
+ { -42, 6, 16, 16, 1, 0, 0, 0 }
+};
+
+typedef struct _decor_color {
+ double r;
+ double g;
+ double b;
+} decor_color_t;
+
+#define IN_EVENT_WINDOW (1 << 0)
+#define PRESSED_EVENT_WINDOW (1 << 1)
+
+typedef struct _decor {
+ Window event_windows[3][3];
+ Window button_windows[3];
+ guint button_states[3];
+ GdkPixmap *pixmap;
+ GdkPixmap *buffer_pixmap;
+ GdkGC *gc;
+ gint width;
+ gint height;
+ gboolean decorated;
+ gboolean active;
+ PangoLayout *layout;
+ gchar *name;
+ cairo_pattern_t *icon;
+ GdkPixmap *icon_pixmap;
+ WnckWindowState state;
+ WnckWindowActions actions;
+ XID prop_xid;
+} decor_t;
+
+typedef void (*event_callback) (WnckWindow *win, XEvent *event);
+
+static GtkWidget *style_window;
+
+static gboolean double_buffering = TRUE;
+static GHashTable *frame_table;
+static GtkWidget *action_menu = NULL;
+static gboolean action_menu_mapped = FALSE;
+static decor_color_t _title_color[2];
+static PangoContext *pango_context;
+static gint double_click_timeout = 250;
+
+static GtkWidget *tip_window;
+static GtkWidget *tip_label;
+static GTimeVal tooltip_last_popdown = { -1, -1 };
+static gint tooltip_timer_tag = 0;
+
+static GSList *draw_list = NULL;
+static guint draw_idle_id = 0;
+
+static void
+send_decor_sync_notify (decor_t *d)
+{
+ Display *xdisplay;
+ GdkDisplay *gdkdisplay;
+ GdkScreen *screen;
+ Window xroot;
+ XEvent ev;
+
+ gdkdisplay = gdk_display_get_default ();
+ xdisplay = GDK_DISPLAY_XDISPLAY (gdkdisplay);
+ screen = gdk_display_get_default_screen (gdkdisplay);
+ xroot = RootWindowOfScreen (gdk_x11_screen_get_xscreen (screen));
+
+ ev.xclient.type = ClientMessage;
+ ev.xclient.display = xdisplay;
+
+ ev.xclient.serial = 0;
+ ev.xclient.send_event = TRUE;
+
+ ev.xclient.window = xroot;
+ ev.xclient.message_type = win_decor_sync_atom;
+ ev.xclient.format = 32;
+
+ ev.xclient.data.l[0] = GDK_PIXMAP_XID (d->pixmap);
+ ev.xclient.data.l[1] = 0;
+ ev.xclient.data.l[2] = 0;
+ ev.xclient.data.l[3] = 0;
+ ev.xclient.data.l[4] = 0;
+
+ XSendEvent (xdisplay, xroot, FALSE,
+ SubstructureRedirectMask | SubstructureNotifyMask,
+ &ev);
+}
+
+/*
+ decoration property
+ -------------------
+
+ data[0] = pixmap
+
+ data[1] = input left
+ data[2] = input right
+ data[3] = input top
+ data[4] = input bottom
+
+ flags
+
+ 1st and 2nd bit p1 gravity, 3rd and 4th bit p2 gravity,
+ 5rd and 6th bit alignment, 7th bit XX, 8th bit XY, 9th bit YX, 10th bit YY.
+
+ data[4 + n * 9 + 1] = flags
+ data[4 + n * 9 + 2] = p1 x
+ data[4 + n * 9 + 3] = p1 y
+ data[4 + n * 9 + 4] = p2 x
+ data[4 + n * 9 + 5] = p2 y
+ data[4 + n * 9 + 6] = widthMax
+ data[4 + n * 9 + 7] = heightMax
+ data[4 + n * 9 + 8] = x0
+ data[4 + n * 9 + 9] = y0
+ */
+static void
+decoration_to_property (long *data,
+ Pixmap pixmap,
+ extents *input,
+ quad *quad,
+ int nQuad)
+{
+ memcpy (data++, &pixmap, sizeof (Pixmap));
+
+ *data++ = input->left;
+ *data++ = input->right;
+ *data++ = input->top;
+ *data++ = input->bottom;
+
+ while (nQuad--)
+ {
+ *data++ =
+ (quad->p1.gravity << 0) |
+ (quad->p2.gravity << 2) |
+ (quad->align << 4) |
+ (quad->m.xx ? XX_MASK : 0) |
+ (quad->m.xy ? XY_MASK : 0) |
+ (quad->m.yx ? YX_MASK : 0) |
+ (quad->m.yy ? YY_MASK : 0);
+
+ *data++ = quad->p1.x;
+ *data++ = quad->p1.y;
+ *data++ = quad->p2.x;
+ *data++ = quad->p2.y;
+ *data++ = quad->max_width;
+ *data++ = quad->max_height;
+ *data++ = quad->m.x0;
+ *data++ = quad->m.y0;
+
+ quad++;
+ }
+}
+
+static void
+decor_update_window_property (decor_t *d)
+{
+ long data[128];
+ Display *xdisplay = GDK_DISPLAY_XDISPLAY (gdk_display_get_default ());
+ gint nQuad = sizeof (_win_quads) / sizeof (_win_quads[0]);
+ gint nButtonQuad = sizeof (_win_button_quads) /
+ sizeof (_win_button_quads[0]);
+ quad quads[nQuad + nButtonQuad];
+
+ memcpy (quads, _win_quads, nQuad * sizeof (quad));
+ memcpy (quads + nQuad, _win_button_quads, nButtonQuad * sizeof (quad));
+
+ quads[2].m.x0 = quads[4].m.x0 = quads[7].m.x0 = d->width - RIGHT_SPACE;
+ quads[1].m.xx = 1.0;
+
+ quads[1].max_width = d->width - LEFT_SPACE - RIGHT_SPACE - BUTTON_SPACE;
+
+ quads[8].p1.x = d->width - LEFT_SPACE - RIGHT_SPACE - BUTTON_SPACE;
+ quads[8].m.x0 = d->width - RIGHT_SPACE - BUTTON_SPACE;
+
+ quads[9].p1.x = d->width - LEFT_SPACE - RIGHT_SPACE - BUTTON_SPACE;
+ quads[9].m.x0 = d->width - RIGHT_SPACE - BUTTON_SPACE;
+
+ quads[9].max_width = BUTTON_SPACE;
+
+ nQuad += nButtonQuad;
+
+ decoration_to_property (data, GDK_PIXMAP_XID (d->pixmap),
+ &_win_extents, quads, nQuad);
+
+ gdk_error_trap_push ();
+ XChangeProperty (xdisplay, d->prop_xid,
+ win_decor_atom,
+ XA_INTEGER,
+ 32, PropModeReplace, (guchar *) data, 5 + 9 * nQuad);
+ XSync (xdisplay, FALSE);
+ gdk_error_trap_pop ();
+}
+
+static void
+gdk_cairo_set_source_color_alpha (cairo_t *cr,
+ GdkColor *color,
+ double alpha)
+{
+ cairo_set_source_rgba (cr,
+ color->red / 65535.0,
+ color->green / 65535.0,
+ color->blue / 65535.0,
+ alpha);
+}
+
+#define CORNER_TOPLEFT (1 << 0)
+#define CORNER_TOPRIGHT (1 << 1)
+#define CORNER_BOTTOMRIGHT (1 << 2)
+#define CORNER_BOTTOMLEFT (1 << 3)
+
+static void
+rounded_rectangle (cairo_t *cr,
+ double x,
+ double y,
+ double w,
+ double h,
+ double radius,
+ int corner)
+{
+ if (corner & CORNER_TOPLEFT)
+ cairo_move_to (cr, x + radius, y);
+ else
+ cairo_move_to (cr, x, y);
+
+ if (corner & CORNER_TOPRIGHT)
+ cairo_arc (cr, x + w - radius, y + radius, radius,
+ M_PI * 1.5, M_PI * 2.0);
+ else
+ cairo_line_to (cr, x + w, y);
+
+ if (corner & CORNER_BOTTOMRIGHT)
+ cairo_arc (cr, x + w - radius, y + h - radius, radius,
+ 0.0, M_PI * 0.5);
+ else
+ cairo_line_to (cr, x + w, y + h);
+
+ if (corner & CORNER_BOTTOMLEFT)
+ cairo_arc (cr, x + radius, y + h - radius, radius,
+ M_PI * 0.5, M_PI);
+ else
+ cairo_line_to (cr, x, y + h);
+
+ if (corner & CORNER_TOPLEFT)
+ cairo_arc (cr, x + radius, y + radius, radius, M_PI, M_PI * 1.5);
+ else
+ cairo_line_to (cr, x, y);
+}
+
+#define SHADE_LEFT (1 << 0)
+#define SHADE_RIGHT (1 << 1)
+#define SHADE_TOP (1 << 2)
+#define SHADE_BOTTOM (1 << 3)
+
+static void
+fill_rounded_rectangle (cairo_t *cr,
+ double x,
+ double y,
+ double w,
+ double h,
+ double radius,
+ int corner,
+ decor_color_t *c0,
+ double alpha0,
+ decor_color_t *c1,
+ double alpha1,
+ int gravity)
+{
+ cairo_pattern_t *pattern;
+
+ rounded_rectangle (cr, x, y, w, h, radius, corner);
+
+ if (gravity & SHADE_RIGHT)
+ {
+ x = x + w;
+ w = -w;
+ }
+ else if (!(gravity & SHADE_LEFT))
+ {
+ x = w = 0;
+ }
+
+ if (gravity & SHADE_BOTTOM)
+ {
+ y = y + h;
+ h = -h;
+ }
+ else if (!(gravity & SHADE_TOP))
+ {
+ y = h = 0;
+ }
+
+ if (w && h)
+ {
+ cairo_matrix_t matrix;
+
+ pattern = cairo_pattern_create_radial (0.0, 0.0, 0.0, 0.0, 0.0, w);
+
+ cairo_matrix_init_scale (&matrix, 1.0, w / h);
+ cairo_matrix_translate (&matrix, -(x + w), -(y + h));
+
+ cairo_pattern_set_matrix (pattern, &matrix);
+ }
+ else
+ {
+ pattern = cairo_pattern_create_linear (x + w, y + h, x, y);
+ }
+
+ cairo_pattern_add_color_stop_rgba (pattern, 0.0, c0->r, c0->g, c0->b,
+ alpha0);
+
+ cairo_pattern_add_color_stop_rgba (pattern, 1.0, c1->r, c1->g, c1->b,
+ alpha1);
+
+ cairo_pattern_set_extend (pattern, CAIRO_EXTEND_PAD);
+
+ cairo_set_source (cr, pattern);
+ cairo_fill (cr);
+ cairo_pattern_destroy (pattern);
+}
+
+static void
+draw_shadow_background (decor_t *d,
+ cairo_t *cr)
+{
+ cairo_matrix_t matrix;
+ double w, h, x2, y2;
+
+ w = d->width - 13.0 - 14.0;
+ h = d->height - 13.0 - 14.0;
+
+ x2 = d->width - 14.0;
+ y2 = d->height - 14.0;
+
+ /* top left */
+ cairo_matrix_init_identity (&matrix);
+ cairo_pattern_set_matrix (shadow_pattern, &matrix);
+ cairo_set_source (cr, shadow_pattern);
+ cairo_rectangle (cr, 0.0, 0.0, 13.0, 13.0);
+ cairo_fill (cr);
+
+ /* top */
+ cairo_matrix_init_translate (&matrix, 13.0, 0.0);
+ cairo_matrix_scale (&matrix, 1.0 / w, 1.0);
+ cairo_matrix_translate (&matrix, -13.0, 0.0);
+ cairo_pattern_set_matrix (shadow_pattern, &matrix);
+ cairo_set_source (cr, shadow_pattern);
+ cairo_rectangle (cr, 13.0, 0.0, w, 13.0);
+ cairo_fill (cr);
+
+ /* top right */
+ cairo_matrix_init_translate (&matrix, 13.0 - x2, 0.0);
+ cairo_pattern_set_matrix (shadow_pattern, &matrix);
+ cairo_set_source (cr, shadow_pattern);
+ cairo_rectangle (cr, x2, 0.0, 14.0, 13.0);
+ cairo_fill (cr);
+
+ /* left */
+ cairo_matrix_init_translate (&matrix, 0.0, 13.0);
+ cairo_matrix_scale (&matrix, 1.0, 1.0 / h);
+ cairo_matrix_translate (&matrix, 0.0, -13.0);
+ cairo_pattern_set_matrix (shadow_pattern, &matrix);
+ cairo_set_source (cr, shadow_pattern);
+ cairo_rectangle (cr, 0.0, 13.0, 13.0, h);
+ cairo_fill (cr);
+
+ /* right */
+ cairo_matrix_init_translate (&matrix, 13.0 - x2, 13.0);
+ cairo_matrix_scale (&matrix, 1.0, 1.0 / h);
+ cairo_matrix_translate (&matrix, 0.0, -13.0);
+ cairo_pattern_set_matrix (shadow_pattern, &matrix);
+ cairo_set_source (cr, shadow_pattern);
+ cairo_rectangle (cr, x2, 13.0, 14.0, h);
+ cairo_fill (cr);
+
+ /* bottom right */
+ cairo_matrix_init_translate (&matrix, 0.0, -15.0);
+ cairo_pattern_set_matrix (shadow_pattern, &matrix);
+ cairo_set_source (cr, shadow_pattern);
+ cairo_rectangle (cr, 0.0, y2, 13.0, 14.0);
+ cairo_fill (cr);
+
+ /* bottom */
+ cairo_matrix_init_translate (&matrix, 13.0, -15.0);
+ cairo_matrix_scale (&matrix, 1.0 / w, 1.0);
+ cairo_matrix_translate (&matrix, -13.0, 0.0);
+ cairo_pattern_set_matrix (shadow_pattern, &matrix);
+ cairo_set_source (cr, shadow_pattern);
+ cairo_rectangle (cr, 13.0, y2, w, 14.0);
+ cairo_fill (cr);
+
+ /* right */
+ cairo_matrix_init_translate (&matrix, 13.0 - x2, 13.0 - y2);
+ cairo_pattern_set_matrix (shadow_pattern, &matrix);
+ cairo_set_source (cr, shadow_pattern);
+ cairo_rectangle (cr, x2, y2, 14.0, 14.0);
+ cairo_fill (cr);
+}
+
+static void
+draw_close_button (decor_t *d,
+ cairo_t *cr,
+ double s)
+{
+ cairo_rel_move_to (cr, 0.0, s);
+
+ cairo_rel_line_to (cr, s, -s);
+ cairo_rel_line_to (cr, s, s);
+ cairo_rel_line_to (cr, s, -s);
+ cairo_rel_line_to (cr, s, s);
+
+ cairo_rel_line_to (cr, -s, s);
+ cairo_rel_line_to (cr, s, s);
+ cairo_rel_line_to (cr, -s, s);
+ cairo_rel_line_to (cr, -s, -s);
+
+ cairo_rel_line_to (cr, -s, s);
+ cairo_rel_line_to (cr, -s, -s);
+ cairo_rel_line_to (cr, s, -s);
+
+ cairo_close_path (cr);
+}
+
+static void
+draw_max_button (decor_t *d,
+ cairo_t *cr,
+ double s)
+{
+ cairo_rel_line_to (cr, 12.0, 0.0);
+ cairo_rel_line_to (cr, 0.0, 12.0);
+ cairo_rel_line_to (cr, -12.0, 0.0);
+
+ cairo_close_path (cr);
+
+ cairo_rel_move_to (cr, 2.0, s);
+
+ cairo_rel_line_to (cr, 12.0 - 4.0, 0.0);
+ cairo_rel_line_to (cr, 0.0, 12.0 - s - 2.0);
+ cairo_rel_line_to (cr, -(12.0 - 4.0), 0.0);
+
+ cairo_close_path (cr);
+}
+
+static void
+draw_unmax_button (decor_t *d,
+ cairo_t *cr,
+ double s)
+{
+ cairo_rel_move_to (cr, 1.0, 1.0);
+
+ cairo_rel_line_to (cr, 10.0, 0.0);
+ cairo_rel_line_to (cr, 0.0, 10.0);
+ cairo_rel_line_to (cr, -10.0, 0.0);
+
+ cairo_close_path (cr);
+
+ cairo_rel_move_to (cr, 2.0, s);
+
+ cairo_rel_line_to (cr, 10.0 - 4.0, 0.0);
+ cairo_rel_line_to (cr, 0.0, 10.0 - s - 2.0);
+ cairo_rel_line_to (cr, -(10.0 - 4.0), 0.0);
+
+ cairo_close_path (cr);
+}
+
+static void
+draw_min_button (decor_t *d,
+ cairo_t *cr,
+ double s)
+{
+ cairo_rel_move_to (cr, 0.0, 8.0);
+
+ cairo_rel_line_to (cr, 12.0, 0.0);
+ cairo_rel_line_to (cr, 0.0, s);
+ cairo_rel_line_to (cr, -12.0, 0.0);
+
+ cairo_close_path (cr);
+}
+
+static void
+button_state_offsets (gdouble x,
+ gdouble y,
+ guint state,
+ gdouble *return_x,
+ gdouble *return_y,
+ gdouble *return_sx,
+ gdouble *return_sy)
+{
+ static double off[] = { 0.0, 0.0, 0.0, 1.0 };
+ static double shadow_off[] = { 1.0, 1.0, 1.0, 0.0 };
+
+ *return_x = x + off[state];
+ *return_y = y + off[state];
+ *return_sx = x + shadow_off[state];
+ *return_sy = y + shadow_off[state];
+}
+
+static void
+draw_window_decoration (decor_t *d)
+{
+ cairo_t *cr;
+ GtkStyle *style;
+ decor_color_t color;
+ double alpha;
+ double x1, y1, x2, y2, x, y, sx, sy;
+ int corners = SHADE_LEFT | SHADE_RIGHT | SHADE_TOP | SHADE_BOTTOM;
+
+ if (!d->pixmap)
+ return;
+
+ style = gtk_widget_get_style (style_window);
+
+ if (d->state & (WNCK_WINDOW_STATE_MAXIMIZED_HORIZONTALLY |
+ WNCK_WINDOW_STATE_MAXIMIZED_VERTICALLY))
+ corners = 0;
+
+ color.r = style->bg[GTK_STATE_NORMAL].red / 65535.0;
+ color.g = style->bg[GTK_STATE_NORMAL].green / 65535.0;
+ color.b = style->bg[GTK_STATE_NORMAL].blue / 65535.0;
+
+ if (d->buffer_pixmap)
+ cr = gdk_cairo_create (GDK_DRAWABLE (d->buffer_pixmap));
+ else
+ cr = gdk_cairo_create (GDK_DRAWABLE (d->pixmap));
+
+ cairo_set_operator (cr, CAIRO_OPERATOR_SOURCE);
+
+ x1 = LEFT_SPACE - _win_extents.left;
+ y1 = TOP_SPACE - _win_extents.top;
+ x2 = d->width - RIGHT_SPACE + _win_extents.right;
+ y2 = d->height - BOTTOM_SPACE + _win_extents.bottom;
+
+ cairo_set_line_width (cr, 1.0);
+
+ draw_shadow_background (d, cr);
+
+ if (d->active)
+ {
+ decor_color_t *title_color = _title_color;
+
+ alpha = 0.8;
+
+ fill_rounded_rectangle (cr,
+ x1 + 0.5,
+ y1 + 0.5,
+ _win_extents.left - 0.5,
+ _win_extents.top - 0.5,
+ 5.0, CORNER_TOPLEFT & corners,
+ &title_color[0], 1.0, &title_color[1], alpha,
+ SHADE_TOP | SHADE_LEFT);
+
+ fill_rounded_rectangle (cr,
+ x1 + _win_extents.left,
+ y1 + 0.5,
+ x2 - x1 - _win_extents.left -
+ _win_extents.right,
+ _win_extents.top - 0.5,
+ 5.0, 0,
+ &title_color[0], 1.0, &title_color[1], alpha,
+ SHADE_TOP);
+
+ fill_rounded_rectangle (cr,
+ x2 - _win_extents.right,
+ y1 + 0.5,
+ _win_extents.right - 0.5,
+ _win_extents.top - 0.5,
+ 5.0, CORNER_TOPRIGHT & corners,
+ &title_color[0], 1.0, &title_color[1], alpha,
+ SHADE_TOP | SHADE_RIGHT);
+ }
+ else
+ {
+ alpha = 0.5;
+
+ fill_rounded_rectangle (cr,
+ x1 + 0.5,
+ y1 + 0.5,
+ _win_extents.left - 0.5,
+ _win_extents.top - 0.5,
+ 5.0, CORNER_TOPLEFT & corners,
+ &color, 1.0, &color, alpha,
+ SHADE_TOP | SHADE_LEFT);
+
+ fill_rounded_rectangle (cr,
+ x1 + _win_extents.left,
+ y1 + 0.5,
+ x2 - x1 - _win_extents.left -
+ _win_extents.right,
+ _win_extents.top - 0.5,
+ 5.0, 0,
+ &color, 1.0, &color, alpha,
+ SHADE_TOP);
+
+ fill_rounded_rectangle (cr,
+ x2 - _win_extents.right,
+ y1 + 0.5,
+ _win_extents.right - 0.5,
+ _win_extents.top - 0.5,
+ 5.0, CORNER_TOPRIGHT & corners,
+ &color, 1.0, &color, alpha,
+ SHADE_TOP | SHADE_RIGHT);
+ }
+
+ fill_rounded_rectangle (cr,
+ x1 + 0.5,
+ y1 + _win_extents.top,
+ _win_extents.left - 0.5,
+ 1,
+ 5.0, 0,
+ &color, 1.0, &color, alpha,
+ SHADE_LEFT);
+
+ fill_rounded_rectangle (cr,
+ x2 - _win_extents.right,
+ y1 + _win_extents.top,
+ _win_extents.right - 0.5,
+ 1,
+ 5.0, 0,
+ &color, 1.0, &color, alpha,
+ SHADE_RIGHT);
+
+
+ fill_rounded_rectangle (cr,
+ x1 + 0.5,
+ y2 - _win_extents.bottom,
+ _win_extents.left - 0.5,
+ _win_extents.bottom - 0.5,
+ 5.0, CORNER_BOTTOMLEFT & corners,
+ &color, 1.0, &color, alpha,
+ SHADE_BOTTOM | SHADE_LEFT);
+
+ fill_rounded_rectangle (cr,
+ x1 + _win_extents.left,
+ y2 - _win_extents.bottom,
+ x2 - x1 - _win_extents.left -
+ _win_extents.right,
+ _win_extents.bottom - 0.5,
+ 5.0, 0,
+ &color, 1.0, &color, alpha,
+ SHADE_BOTTOM);
+
+ fill_rounded_rectangle (cr,
+ x2 - _win_extents.right,
+ y2 - _win_extents.bottom,
+ _win_extents.right - 0.5,
+ _win_extents.bottom - 0.5,
+ 5.0, CORNER_BOTTOMRIGHT & corners,
+ &color, 1.0, &color, alpha,
+ SHADE_BOTTOM | SHADE_RIGHT);
+
+ cairo_rectangle (cr,
+ LEFT_SPACE, TOP_SPACE,
+ d->width - LEFT_SPACE - RIGHT_SPACE, 1);
+ gdk_cairo_set_source_color (cr, &style->bg[GTK_STATE_NORMAL]);
+ cairo_fill (cr);
+
+ cairo_set_operator (cr, CAIRO_OPERATOR_OVER);
+
+ if (d->active)
+ {
+ gdk_cairo_set_source_color_alpha (cr,
+ &style->fg[GTK_STATE_NORMAL],
+ 0.7);
+
+ cairo_move_to (cr, x1 + 0.5, y1 + _win_extents.top - 0.5);
+ cairo_rel_line_to (cr, x2 - x1 - 1.0, 0.0);
+
+ cairo_stroke (cr);
+ }
+
+ rounded_rectangle (cr,
+ x1 + 0.5, y1 + 0.5,
+ x2 - x1 - 1.0, y2 - y1 - 1.0,
+ 5.0,
+ (CORNER_TOPLEFT | CORNER_TOPRIGHT | CORNER_BOTTOMLEFT |
+ CORNER_BOTTOMRIGHT) & corners);
+
+ cairo_clip (cr);
+
+ cairo_translate (cr, 1.0, 1.0);
+
+ rounded_rectangle (cr,
+ x1 + 0.5, y1 + 0.5,
+ x2 - x1 - 1.0, y2 - y1 - 1.0,
+ 5.0,
+ (CORNER_TOPLEFT | CORNER_TOPRIGHT | CORNER_BOTTOMLEFT |
+ CORNER_BOTTOMRIGHT) & corners);
+
+ cairo_set_source_rgba (cr, 1.0, 1.0, 1.0, 0.4);
+ cairo_stroke (cr);
+
+ cairo_translate (cr, -2.0, -2.0);
+
+ rounded_rectangle (cr,
+ x1 + 0.5, y1 + 0.5,
+ x2 - x1 - 1.0, y2 - y1 - 1.0,
+ 5.0,
+ (CORNER_TOPLEFT | CORNER_TOPRIGHT | CORNER_BOTTOMLEFT |
+ CORNER_BOTTOMRIGHT) & corners);
+
+ cairo_set_source_rgba (cr, 0.0, 0.0, 0.0, 0.1);
+ cairo_stroke (cr);
+
+ cairo_translate (cr, 1.0, 1.0);
+
+ cairo_reset_clip (cr);
+
+ rounded_rectangle (cr,
+ x1 + 0.5, y1 + 0.5,
+ x2 - x1 - 1.0, y2 - y1 - 1.0,
+ 5.0,
+ (CORNER_TOPLEFT | CORNER_TOPRIGHT | CORNER_BOTTOMLEFT |
+ CORNER_BOTTOMRIGHT) & corners);
+
+ gdk_cairo_set_source_color_alpha (cr,
+ &style->fg[GTK_STATE_NORMAL],
+ alpha);
+
+ cairo_stroke (cr);
+
+ if (d->actions & WNCK_WINDOW_ACTION_CLOSE)
+ {
+ button_state_offsets (d->width - RIGHT_SPACE - BUTTON_SPACE + 39.0,
+ 11.0, d->button_states[0], &x, &y, &sx, &sy);
+
+ if (d->active)
+ {
+ gdk_cairo_set_source_color_alpha (cr,
+ &style->fg[GTK_STATE_NORMAL],
+ alpha);
+ cairo_move_to (cr, sx, sy);
+ draw_close_button (d, cr, 3.0);
+ cairo_fill (cr);
+
+ if (d->button_states[0] & IN_EVENT_WINDOW)
+ cairo_set_source_rgb (cr, 1.0, 1.0, 1.0);
+ else
+ cairo_set_source_rgba (cr, color.r, color.g, color.b, 0.95);
+
+ cairo_move_to (cr, x, y);
+ draw_close_button (d, cr, 3.0);
+ cairo_fill (cr);
+ }
+ else
+ {
+ gdk_cairo_set_source_color_alpha (cr,
+ &style->fg[GTK_STATE_NORMAL],
+ alpha * 0.75);
+ cairo_move_to (cr, x, y);
+ draw_close_button (d, cr, 3.0);
+ cairo_fill (cr);
+ }
+ }
+
+ if (d->actions & WNCK_WINDOW_ACTION_MAXIMIZE)
+ {
+ button_state_offsets (d->width - RIGHT_SPACE - BUTTON_SPACE + 21.0,
+ 11.0, d->button_states[1], &x, &y, &sx, &sy);
+
+ cairo_set_fill_rule (cr, CAIRO_FILL_RULE_EVEN_ODD);
+
+ if (d->active)
+ {
+ gdk_cairo_set_source_color_alpha (cr,
+ &style->fg[GTK_STATE_NORMAL],
+ alpha);
+ cairo_move_to (cr, sx, sy);
+
+ if (d->state & (WNCK_WINDOW_STATE_MAXIMIZED_HORIZONTALLY |
+ WNCK_WINDOW_STATE_MAXIMIZED_VERTICALLY))
+ draw_unmax_button (d, cr, 4.0);
+ else
+ draw_max_button (d, cr, 4.0);
+
+ cairo_fill (cr);
+
+ if (d->button_states[1] & IN_EVENT_WINDOW)
+ cairo_set_source_rgb (cr, 1.0, 1.0, 1.0);
+ else
+ cairo_set_source_rgba (cr, color.r, color.g, color.b, 0.95);
+
+ cairo_move_to (cr, x, y);
+
+ if (d->state & (WNCK_WINDOW_STATE_MAXIMIZED_HORIZONTALLY |
+ WNCK_WINDOW_STATE_MAXIMIZED_VERTICALLY))
+ draw_unmax_button (d, cr, 4.0);
+ else
+ draw_max_button (d, cr, 4.0);
+
+ cairo_fill (cr);
+ }
+ else
+ {
+ gdk_cairo_set_source_color_alpha (cr,
+ &style->fg[GTK_STATE_NORMAL],
+ alpha * 0.75);
+ cairo_move_to (cr, x, y);
+
+ if (d->state & (WNCK_WINDOW_STATE_MAXIMIZED_HORIZONTALLY |
+ WNCK_WINDOW_STATE_MAXIMIZED_VERTICALLY))
+ draw_unmax_button (d, cr, 4.0);
+ else
+ draw_max_button (d, cr, 4.0);
+
+ cairo_fill (cr);
+ }
+ }
+
+ if (d->actions & WNCK_WINDOW_ACTION_MINIMIZE)
+ {
+ button_state_offsets (d->width - RIGHT_SPACE - BUTTON_SPACE + 3.0,
+ 11.0, d->button_states[2], &x, &y, &sx, &sy);
+
+ if (d->active)
+ {
+ gdk_cairo_set_source_color_alpha (cr,
+ &style->fg[GTK_STATE_NORMAL],
+ alpha);
+ cairo_move_to (cr, sx, sy);
+ draw_min_button (d, cr, 4.0);
+ cairo_fill (cr);
+
+ if (d->button_states[2] & IN_EVENT_WINDOW)
+ cairo_set_source_rgb (cr, 1.0, 1.0, 1.0);
+ else
+ cairo_set_source_rgba (cr, color.r, color.g, color.b, 0.95);
+
+ cairo_move_to (cr, x, y);
+ draw_min_button (d, cr, 4.0);
+ cairo_fill (cr);
+ }
+ else
+ {
+ gdk_cairo_set_source_color_alpha (cr,
+ &style->fg[GTK_STATE_NORMAL],
+ alpha * 0.75);
+ cairo_move_to (cr, x, y);
+ draw_min_button (d, cr, 4.0);
+ cairo_fill (cr);
+ }
+ }
+
+ if (d->layout)
+ {
+ gdk_cairo_set_source_color_alpha (cr,
+ &style->fg[GTK_STATE_NORMAL],
+ alpha);
+
+ if (d->active)
+ {
+ cairo_move_to (cr, 33.0, 9.0);
+
+ pango_cairo_show_layout (cr, d->layout);
+
+ cairo_set_source_rgb (cr, 1.0, 1.0, 1.0);
+ }
+
+ cairo_move_to (cr, 32.0, 8.0);
+
+ pango_cairo_show_layout (cr, d->layout);
+ }
+
+ if (d->icon)
+ {
+ cairo_translate (cr, LEFT_SPACE, 9.0);
+ cairo_set_source (cr, d->icon);
+ cairo_rectangle (cr, 0.0, 0.0, 16.0, 16.0);
+ cairo_clip (cr);
+
+ if (d->active)
+ cairo_paint (cr);
+ else
+ cairo_paint_with_alpha (cr, alpha);
+ }
+
+ cairo_destroy (cr);
+
+ if (d->buffer_pixmap)
+ gdk_draw_drawable (d->pixmap,
+ d->gc,
+ d->buffer_pixmap,
+ 0,
+ 0,
+ 0,
+ 0,
+ d->width,
+ d->height);
+
+ if (d->prop_xid)
+ {
+ decor_update_window_property (d);
+ d->prop_xid = 0;
+ }
+
+ send_decor_sync_notify (d);
+}
+
+static gboolean
+draw_decor_list (void *data)
+{
+ GSList *list;
+
+ draw_idle_id = 0;
+
+ for (list = draw_list; list; list = list->next)
+ draw_window_decoration ((decor_t *) list->data);
+
+ g_slist_free (draw_list);
+ draw_list = NULL;
+
+ return FALSE;
+}
+
+static void
+queue_decor_draw (decor_t *d)
+{
+ if (g_slist_find (draw_list, d))
+ return;
+
+ draw_list = g_slist_append (draw_list, d);
+
+ if (!draw_idle_id)
+ draw_idle_id = g_idle_add (draw_decor_list, NULL);
+}
+
+static GdkPixmap *
+create_pixmap (int w,
+ int h)
+{
+ GdkPixmap *pixmap;
+ GdkVisual *visual;
+ GdkColormap *colormap;
+
+ visual = gdk_visual_get_best_with_depth (32);
+ if (!visual)
+ return NULL;
+
+ pixmap = gdk_pixmap_new (NULL, w, h, 32);
+ if (!pixmap)
+ return NULL;
+
+ colormap = gdk_colormap_new (visual, FALSE);
+ if (!colormap)
+ {
+ gdk_pixmap_unref (pixmap);
+ return NULL;
+ }
+
+ gdk_drawable_set_colormap (GDK_DRAWABLE (pixmap), colormap);
+ gdk_colormap_unref (colormap);
+
+ return pixmap;
+}
+
+static GdkPixmap *
+pixmap_new_from_pixbuf (GdkPixbuf *pixbuf)
+{
+ GdkPixmap *pixmap;
+ guint width, height;
+ cairo_t *cr;
+
+ width = gdk_pixbuf_get_width (pixbuf);
+ height = gdk_pixbuf_get_height (pixbuf);
+
+ pixmap = create_pixmap (width, height);
+ if (!pixmap)
+ return NULL;
+
+ cr = (cairo_t *) gdk_cairo_create (GDK_DRAWABLE (pixmap));
+ gdk_cairo_set_source_pixbuf (cr, pixbuf, 0, 0);
+ cairo_set_operator (cr, CAIRO_OPERATOR_SOURCE);
+ cairo_paint (cr);
+ cairo_destroy (cr);
+
+ return pixmap;
+}
+
+static GdkPixmap *
+pixmap_new_from_inline (const guint8 *data)
+{
+ GdkPixbuf *pixbuf;
+
+ pixbuf = gdk_pixbuf_new_from_inline (-1, data, FALSE, NULL);
+ if (!pixbuf)
+ return NULL;
+
+ return pixmap_new_from_pixbuf (pixbuf);
+}
+
+static void
+update_default_decorations (GdkScreen *screen)
+{
+ long data[128];
+ Window xroot;
+ GdkDisplay *gdkdisplay = gdk_display_get_default ();
+ Display *xdisplay = gdk_x11_display_get_xdisplay (gdkdisplay);
+ Atom atom;
+ int nQuad;
+ decor_t d;
+
+ xroot = RootWindowOfScreen (gdk_x11_screen_get_xscreen (screen));
+
+ atom = XInternAtom (xdisplay, "_NET_WINDOW_DECOR_BARE", FALSE);
+ nQuad = sizeof (_shadow_quads) / sizeof (_shadow_quads[0]);
+ decoration_to_property (data, GDK_PIXMAP_XID (shadow_pixmap),
+ &_shadow_extents, _shadow_quads, nQuad);
+
+ XChangeProperty (xdisplay, xroot,
+ atom,
+ XA_INTEGER,
+ 32, PropModeReplace, (guchar *) data, 5 + 9 * nQuad);
+
+ d.width = LEFT_SPACE + 1 + RIGHT_SPACE;
+ d.height = TOP_SPACE + 1 + BOTTOM_SPACE;
+
+ if (!decor_normal_pixmap)
+ decor_normal_pixmap = create_pixmap (d.width, d.height);
+
+ if (decor_normal_pixmap)
+ {
+ d.pixmap = decor_normal_pixmap;
+ d.buffer_pixmap = NULL;
+ d.active = FALSE;
+ d.layout = NULL;
+ d.icon = NULL;
+ d.state = 0;
+ d.actions = 0;
+ d.prop_xid = 0;
+
+ draw_window_decoration (&d);
+
+ atom = XInternAtom (xdisplay, "_NET_WINDOW_DECOR_NORMAL", FALSE);
+ nQuad = sizeof (_win_quads) / sizeof (_win_quads[0]);
+ decoration_to_property (data, GDK_PIXMAP_XID (d.pixmap),
+ &_win_extents, _win_quads, nQuad);
+
+ XChangeProperty (xdisplay, xroot,
+ atom,
+ XA_INTEGER,
+ 32, PropModeReplace, (guchar *) data, 5 + 9 * nQuad);
+ }
+
+ if (!decor_active_pixmap)
+ decor_active_pixmap = create_pixmap (d.width, d.height);
+
+ if (decor_active_pixmap)
+ {
+ d.pixmap = decor_active_pixmap;
+ d.buffer_pixmap = NULL;
+ d.active = TRUE;
+ d.layout = NULL;
+ d.icon = NULL;
+ d.state = 0;
+ d.actions = 0;
+ d.prop_xid = 0;
+
+ draw_window_decoration (&d);
+
+ atom = XInternAtom (xdisplay, "_NET_WINDOW_DECOR_ACTIVE", FALSE);
+ nQuad = sizeof (_win_quads) / sizeof (_win_quads[0]);
+ decoration_to_property (data, GDK_PIXMAP_XID (d.pixmap),
+ &_win_extents, _win_quads, nQuad);
+
+ XChangeProperty (xdisplay, xroot,
+ atom,
+ XA_INTEGER,
+ 32, PropModeReplace, (guchar *) data, 5 + 9 * nQuad);
+ }
+}
+
+static void
+set_dm_check_hint (GdkScreen *screen)
+{
+ XSetWindowAttributes attrs;
+ unsigned long data[1];
+ Window xroot;
+ GdkDisplay *gdkdisplay = gdk_display_get_default ();
+ Display *xdisplay = gdk_x11_display_get_xdisplay (gdkdisplay);
+ Atom atom;
+
+ attrs.override_redirect = TRUE;
+ attrs.event_mask = PropertyChangeMask;
+
+ xroot = RootWindowOfScreen (gdk_x11_screen_get_xscreen (screen));
+
+ data[0] = XCreateWindow (xdisplay,
+ xroot,
+ -100, -100, 1, 1,
+ 0,
+ CopyFromParent,
+ CopyFromParent,
+ (Visual *) CopyFromParent,
+ CWOverrideRedirect | CWEventMask,
+ &attrs);
+
+ atom = XInternAtom (xdisplay, "_NET_SUPPORTING_DM_CHECK", FALSE);
+
+ XChangeProperty (xdisplay, xroot,
+ atom,
+ XA_WINDOW,
+ 32, PropModeReplace, (guchar *) data, 1);
+}
+
+static gboolean
+check_dm_hint (GdkScreen *screen)
+{
+ Window xroot;
+ GdkDisplay *gdkdisplay = gdk_display_get_default ();
+ Display *xdisplay = gdk_x11_display_get_xdisplay (gdkdisplay);
+ Atom actual;
+ int result, format;
+ unsigned long n, left;
+ unsigned char *data;
+ Atom atom;
+ gboolean dm = FALSE;
+
+ xroot = RootWindowOfScreen (gdk_x11_screen_get_xscreen (screen));
+
+ atom = XInternAtom (xdisplay, "_NET_SUPPORTING_DM_CHECK", FALSE);
+
+ result = XGetWindowProperty (xdisplay, xroot,
+ atom, 0L, 1L, FALSE,
+ XA_WINDOW, &actual, &format,
+ &n, &left, &data);
+
+ if (result == Success && n && data)
+ {
+ XWindowAttributes attr;
+ Window window;
+
+ memcpy (&window, data, sizeof (Window));
+
+ XFree (data);
+
+ gdk_error_trap_push ();
+
+ XGetWindowAttributes (xdisplay, window, &attr);
+ XSync (xdisplay, FALSE);
+
+ if (!gdk_error_trap_pop ())
+ dm = TRUE;
+ }
+
+ return dm;
+}
+
+static gboolean
+get_window_prop (Window xwindow,
+ Atom atom,
+ Window *val)
+{
+ Atom type;
+ int format;
+ gulong nitems;
+ gulong bytes_after;
+ Window *w;
+ int err, result;
+
+ *val = 0;
+
+ gdk_error_trap_push ();
+
+ type = None;
+ result = XGetWindowProperty (gdk_display,
+ xwindow,
+ atom,
+ 0, G_MAXLONG,
+ False, XA_WINDOW, &type, &format, &nitems,
+ &bytes_after, (void*) &w);
+ err = gdk_error_trap_pop ();
+ if (err != Success || result != Success)
+ return FALSE;
+
+ if (type != XA_WINDOW)
+ {
+ XFree (w);
+ return FALSE;
+ }
+
+ *val = *w;
+ XFree (w);
+
+ return TRUE;
+}
+
+static void
+update_event_windows (WnckWindow *win)
+{
+ Display *xdisplay;
+ decor_t *d = g_object_get_data (G_OBJECT (win), "decor");
+ gint x0, y0, width, height, x, y, w, h;
+ gint i, j;
+
+ xdisplay = GDK_DISPLAY_XDISPLAY (gdk_display_get_default ());
+
+ wnck_window_get_geometry (win, &x0, &y0, &width, &height);
+
+ gdk_error_trap_push ();
+
+ for (i = 0; i < 3; i++)
+ {
+ static guint event_window_actions[3][3] = {
+ {
+ WNCK_WINDOW_ACTION_RESIZE,
+ WNCK_WINDOW_ACTION_RESIZE,
+ WNCK_WINDOW_ACTION_RESIZE
+ }, {
+ WNCK_WINDOW_ACTION_RESIZE,
+ WNCK_WINDOW_ACTION_MOVE,
+ WNCK_WINDOW_ACTION_RESIZE
+ }, {
+ WNCK_WINDOW_ACTION_RESIZE,
+ WNCK_WINDOW_ACTION_RESIZE,
+ WNCK_WINDOW_ACTION_RESIZE
+ }
+ };
+
+ for (j = 0; j < 3; j++)
+ {
+ if (d->actions & event_window_actions[i][j])
+ {
+ x = pos[i][j].x + pos[i][j].xw * width;
+ y = pos[i][j].y + pos[i][j].yh * height;
+ w = pos[i][j].w + pos[i][j].ww * width;
+ h = pos[i][j].h + pos[i][j].hh * height;
+
+ XMapWindow (xdisplay, d->event_windows[i][j]);
+ XMoveResizeWindow (xdisplay, d->event_windows[i][j],
+ x, y, w, h);
+ }
+ else
+ {
+ XUnmapWindow (xdisplay, d->event_windows[i][j]);
+ }
+ }
+ }
+
+ for (i = 0; i < 3; i++)
+ {
+ static guint button_actions[3] = {
+ WNCK_WINDOW_ACTION_CLOSE,
+ WNCK_WINDOW_ACTION_MAXIMIZE,
+ WNCK_WINDOW_ACTION_MINIMIZE
+ };
+
+ if (d->actions & button_actions[i])
+ {
+ x = bpos[i].x + bpos[i].xw * width;
+ y = bpos[i].y + bpos[i].yh * height;
+ w = bpos[i].w + bpos[i].ww * width;
+ h = bpos[i].h + bpos[i].hh * height;
+
+ XMapWindow (xdisplay, d->button_windows[i]);
+ XMoveResizeWindow (xdisplay, d->button_windows[i], x, y, w, h);
+ }
+ else
+ XUnmapWindow (xdisplay, d->button_windows[i]);
+ }
+
+ XSync (xdisplay, FALSE);
+ gdk_error_trap_pop ();
+}
+
+static gint
+max_window_name_width (WnckWindow *win)
+{
+ decor_t *d = g_object_get_data (G_OBJECT (win), "decor");
+ const gchar *name;
+ gint w;
+
+ name = wnck_window_get_name (win);
+ if (!name)
+ return 0;
+
+ if (!d->layout)
+ {
+ d->layout = pango_layout_new (pango_context);
+ if (!d->layout)
+ return 0;
+
+ pango_layout_set_wrap (d->layout, PANGO_WRAP_CHAR);
+ }
+
+ pango_layout_set_width (d->layout, -1);
+ pango_layout_set_text (d->layout, name, strlen (name));
+ pango_layout_get_pixel_size (d->layout, &w, NULL);
+
+ if (d->name)
+ pango_layout_set_text (d->layout, d->name, strlen (d->name));
+
+ return w + 4;
+}
+
+static void
+update_window_decoration_name (WnckWindow *win)
+{
+ decor_t *d = g_object_get_data (G_OBJECT (win), "decor");
+ const gchar *name;
+ glong name_length;
+ PangoLayoutLine *line;
+
+ if (d->name)
+ {
+ g_free (d->name);
+ d->name = NULL;
+ }
+
+ name = wnck_window_get_name (win);
+ if (name && (name_length = strlen (name)))
+ {
+ gint w, n_line;
+
+ w = d->width - LEFT_SPACE - RIGHT_SPACE - BUTTON_SPACE - ICON_SPACE - 4;
+ if (w < 1)
+ w = 1;
+
+ pango_layout_set_width (d->layout, w * PANGO_SCALE);
+ pango_layout_set_text (d->layout, name, name_length);
+
+ n_line = pango_layout_get_line_count (d->layout);
+
+ line = pango_layout_get_line (d->layout, 0);
+
+ name_length = line->length;
+ if (pango_layout_get_line_count (d->layout) > 1)
+ {
+ if (name_length < 4)
+ {
+ g_object_unref (G_OBJECT (d->layout));
+ d->layout = NULL;
+ return;
+ }
+
+ d->name = g_strndup (name, name_length);
+ strcpy (d->name + name_length - 3, "...");
+ }
+ else
+ d->name = g_strndup (name, name_length);
+
+ pango_layout_set_text (d->layout, d->name, name_length);
+ }
+ else if (d->layout)
+ {
+ g_object_unref (G_OBJECT (d->layout));
+ d->layout = NULL;
+ }
+}
+
+static void
+update_window_decoration_icon (WnckWindow *win)
+{
+ decor_t *d = g_object_get_data (G_OBJECT (win), "decor");
+ GdkPixbuf *icon;
+
+ if (d->icon)
+ {
+ cairo_pattern_destroy (d->icon);
+ d->icon = NULL;
+ }
+
+ if (d->icon_pixmap)
+ {
+ gdk_pixmap_unref (d->icon_pixmap);
+ d->icon_pixmap = NULL;
+ }
+
+ icon = wnck_window_get_mini_icon (win);
+ if (icon)
+ {
+ cairo_t *cr;
+
+ d->icon_pixmap = pixmap_new_from_pixbuf (icon);
+ cr = gdk_cairo_create (GDK_DRAWABLE (d->icon_pixmap));
+ d->icon = cairo_pattern_create_for_surface (cairo_get_target (cr));
+ cairo_destroy (cr);
+ }
+}
+
+static void
+update_window_decoration_state (WnckWindow *win)
+{
+ decor_t *d = g_object_get_data (G_OBJECT (win), "decor");
+
+ d->state = wnck_window_get_state (win);
+}
+
+static void
+update_window_decoration_actions (WnckWindow *win)
+{
+ decor_t *d = g_object_get_data (G_OBJECT (win), "decor");
+
+ d->actions = wnck_window_get_actions (win);
+}
+
+static gboolean
+update_window_decoration_size (WnckWindow *win)
+{
+ decor_t *d = g_object_get_data (G_OBJECT (win), "decor");
+ GdkPixmap *pixmap, *buffer_pixmap = NULL;
+ gint width, height;
+ gint w;
+
+ width = max_window_name_width (win) + BUTTON_SPACE + ICON_SPACE;
+
+ wnck_window_get_geometry (win, NULL, NULL, &w, NULL);
+ if (w < width)
+ width = MAX (ICON_SPACE + BUTTON_SPACE, w);
+
+ width += LEFT_SPACE + RIGHT_SPACE;
+ height = TOP_SPACE + 1 + BOTTOM_SPACE;
+
+ if (width == d->width && height == d->height)
+ return FALSE;
+
+ pixmap = create_pixmap (width, height);
+ if (!pixmap)
+ return FALSE;
+
+ if (double_buffering)
+ {
+ buffer_pixmap = create_pixmap (width, height);
+ if (!buffer_pixmap)
+ {
+ gdk_pixmap_unref (pixmap);
+ return FALSE;
+ }
+ }
+
+ if (d->pixmap)
+ gdk_pixmap_unref (d->pixmap);
+
+ if (d->buffer_pixmap)
+ gdk_pixmap_unref (d->buffer_pixmap);
+
+ if (d->gc)
+ gdk_gc_unref (d->gc);
+
+ d->pixmap = pixmap;
+ d->buffer_pixmap = buffer_pixmap;
+ d->gc = gdk_gc_new (pixmap);
+
+ d->width = width;
+ d->height = height;
+
+ d->prop_xid = wnck_window_get_xid (win);
+
+ update_window_decoration_name (win);
+
+ queue_decor_draw (d);
+
+ return TRUE;
+}
+
+static void
+add_frame_window (WnckWindow *win,
+ Window frame)
+{
+ Display *xdisplay;
+ XSetWindowAttributes attr;
+ gulong xid = wnck_window_get_xid (win);
+ decor_t *d = g_object_get_data (G_OBJECT (win), "decor");
+ gint i, j;
+
+ xdisplay = GDK_DISPLAY_XDISPLAY (gdk_display_get_default ());
+
+ attr.event_mask = ButtonPressMask | EnterWindowMask | LeaveWindowMask;
+ attr.override_redirect = TRUE;
+
+ gdk_error_trap_push ();
+
+ for (i = 0; i < 3; i++)
+ {
+ for (j = 0; j < 3; j++)
+ {
+ d->event_windows[i][j] =
+ XCreateWindow (xdisplay,
+ frame,
+ 0, 0, 1, 1, 0,
+ CopyFromParent, CopyFromParent, CopyFromParent,
+ CWOverrideRedirect | CWEventMask, &attr);
+
+ if (cursor[i][j].cursor)
+ XDefineCursor (xdisplay, d->event_windows[i][j],
+ cursor[i][j].cursor);
+ }
+ }
+
+ attr.event_mask |= ButtonReleaseMask;
+
+ for (i = 0; i < 3; i++)
+ {
+ d->button_windows[i] =
+ XCreateWindow (xdisplay,
+ frame,
+ 0, 0, 1, 1, 0,
+ CopyFromParent, CopyFromParent, CopyFromParent,
+ CWOverrideRedirect | CWEventMask, &attr);
+
+ d->button_states[i] = 0;
+ }
+
+ XSync (xdisplay, FALSE);
+ if (!gdk_error_trap_pop ())
+ {
+ d->decorated = TRUE;
+
+ for (i = 0; i < 3; i++)
+ for (j = 0; j < 3; j++)
+ g_hash_table_insert (frame_table,
+ GINT_TO_POINTER (d->event_windows[i][j]),
+ GINT_TO_POINTER (xid));
+
+ for (i = 0; i < 3; i++)
+ g_hash_table_insert (frame_table,
+ GINT_TO_POINTER (d->button_windows[i]),
+ GINT_TO_POINTER (xid));
+
+
+ update_window_decoration_state (win);
+ update_window_decoration_actions (win);
+ update_window_decoration_icon (win);
+ update_window_decoration_size (win);
+
+ update_event_windows (win);
+ }
+ else
+ {
+ memset (d->event_windows, 0, sizeof (d->event_windows));
+ }
+}
+
+static void
+remove_frame_window (WnckWindow *win)
+{
+ decor_t *d = g_object_get_data (G_OBJECT (win), "decor");
+
+ if (d->pixmap)
+ {
+ gdk_pixmap_unref (d->pixmap);
+ d->pixmap = NULL;
+ }
+
+ if (d->buffer_pixmap)
+ {
+ gdk_pixmap_unref (d->buffer_pixmap);
+ d->buffer_pixmap = NULL;
+ }
+
+ if (d->gc)
+ {
+ gdk_gc_unref (d->gc);
+ d->gc = NULL;
+ }
+
+ if (d->name)
+ {
+ g_free (d->name);
+ d->name = NULL;
+ }
+
+ if (d->layout)
+ {
+ g_object_unref (G_OBJECT (d->layout));
+ d->layout = NULL;
+ }
+
+ if (d->icon)
+ {
+ cairo_pattern_destroy (d->icon);
+ d->icon = NULL;
+ }
+
+ if (d->icon_pixmap)
+ {
+ gdk_pixmap_unref (d->icon_pixmap);
+ d->icon_pixmap = NULL;
+ }
+
+ d->width = 0;
+ d->height = 0;
+
+ d->decorated = FALSE;
+
+ d->state = 0;
+ d->actions = 0;
+
+ draw_list = g_slist_remove (draw_list, d);
+}
+
+static void
+window_name_changed (WnckWindow *win)
+{
+ decor_t *d = g_object_get_data (G_OBJECT (win), "decor");
+
+ if (d->decorated)
+ {
+ if (update_window_decoration_size (win))
+ queue_decor_draw (d);
+ }
+}
+
+static void
+window_geometry_changed (WnckWindow *win)
+{
+ decor_t *d = g_object_get_data (G_OBJECT (win), "decor");
+
+ if (d->decorated)
+ {
+ update_window_decoration_size (win);
+ update_event_windows (win);
+ }
+}
+
+static void
+window_icon_changed (WnckWindow *win)
+{
+ decor_t *d = g_object_get_data (G_OBJECT (win), "decor");
+
+ if (d->decorated)
+ {
+ update_window_decoration_icon (win);
+ queue_decor_draw (d);
+ }
+}
+
+static void
+window_state_changed (WnckWindow *win)
+{
+ decor_t *d = g_object_get_data (G_OBJECT (win), "decor");
+
+ if (d->decorated)
+ {
+ update_window_decoration_state (win);
+ queue_decor_draw (d);
+ }
+}
+
+static void
+window_actions_changed (WnckWindow *win)
+{
+ decor_t *d = g_object_get_data (G_OBJECT (win), "decor");
+
+ if (d->decorated)
+ {
+ update_window_decoration_actions (win);
+ queue_decor_draw (d);
+ }
+}
+
+static void
+connect_window (WnckWindow *win)
+{
+ g_signal_connect_object (win, "name_changed",
+ G_CALLBACK (window_name_changed),
+ 0, 0);
+ g_signal_connect_object (win, "geometry_changed",
+ G_CALLBACK (window_geometry_changed),
+ 0, 0);
+ g_signal_connect_object (win, "icon_changed",
+ G_CALLBACK (window_icon_changed),
+ 0, 0);
+ g_signal_connect_object (win, "state_changed",
+ G_CALLBACK (window_state_changed),
+ 0, 0);
+ g_signal_connect_object (win, "actions_changed",
+ G_CALLBACK (window_actions_changed),
+ 0, 0);
+}
+
+static void
+active_window_changed (WnckScreen *screen)
+{
+ WnckWindow *win;
+ decor_t *d;
+
+ win = wnck_screen_get_previously_active_window (screen);
+ if (win)
+ {
+ d = g_object_get_data (G_OBJECT (win), "decor");
+ if (d->pixmap)
+ {
+ d->active = wnck_window_is_active (win);
+ queue_decor_draw (d);
+ }
+ }
+
+ win = wnck_screen_get_active_window (screen);
+ if (win)
+ {
+ d = g_object_get_data (G_OBJECT (win), "decor");
+ if (d->pixmap)
+ {
+ d->active = wnck_window_is_active (win);
+ queue_decor_draw (d);
+ }
+ }
+}
+
+static void
+window_opened (WnckScreen *screen,
+ WnckWindow *win)
+{
+ decor_t *d;
+ Window frame;
+ gulong xid;
+
+ d = g_malloc (sizeof (decor_t));
+ if (!d)
+ return;
+
+ d->pixmap = NULL;
+ d->buffer_pixmap = NULL;
+ d->gc = NULL;
+
+ d->icon = NULL;
+ d->icon_pixmap = NULL;
+
+ d->width = 0;
+ d->height = 0;
+
+ d->active = wnck_window_is_active (win);
+
+ d->layout = NULL;
+ d->name = NULL;
+
+ d->state = 0;
+ d->actions = 0;
+
+ d->prop_xid = 0;
+
+ d->decorated = FALSE;
+
+ g_object_set_data (G_OBJECT (win), "decor", d);
+
+ connect_window (win);
+
+ xid = wnck_window_get_xid (win);
+
+ if (get_window_prop (xid, frame_window_atom, &frame))
+ add_frame_window (win, frame);
+}
+
+static void
+window_closed (WnckScreen *screen,
+ WnckWindow *win)
+{
+ decor_t *d = g_object_get_data (G_OBJECT (win), "decor");
+
+ remove_frame_window (win);
+
+ g_free (d);
+}
+
+static void
+connect_screen (WnckScreen *screen)
+{
+ GList *windows;
+
+ g_signal_connect_object (G_OBJECT (screen), "active_window_changed",
+ G_CALLBACK (active_window_changed),
+ 0, 0);
+ g_signal_connect_object (G_OBJECT (screen), "window_opened",
+ G_CALLBACK (window_opened),
+ 0, 0);
+ g_signal_connect_object (G_OBJECT (screen), "window_closed",
+ G_CALLBACK (window_closed),
+ 0, 0);
+
+ windows = wnck_screen_get_windows (screen);
+ while (windows != NULL)
+ {
+ window_opened (screen, windows->data);
+ windows = windows->next;
+ }
+}
+
+static void
+move_resize_window (WnckWindow *win,
+ int direction,
+ XEvent *xevent)
+{
+ Display *xdisplay;
+ GdkDisplay *gdkdisplay;
+ GdkScreen *screen;
+ Window xroot;
+ XEvent ev;
+
+ gdkdisplay = gdk_display_get_default ();
+ xdisplay = GDK_DISPLAY_XDISPLAY (gdkdisplay);
+ screen = gdk_display_get_default_screen (gdkdisplay);
+ xroot = RootWindowOfScreen (gdk_x11_screen_get_xscreen (screen));
+
+ if (action_menu_mapped)
+ {
+ gtk_object_destroy (GTK_OBJECT (action_menu));
+ action_menu_mapped = FALSE;
+ action_menu = NULL;
+ return;
+ }
+
+ ev.xclient.type = ClientMessage;
+ ev.xclient.display = xdisplay;
+
+ ev.xclient.serial = 0;
+ ev.xclient.send_event = TRUE;
+
+ ev.xclient.window = wnck_window_get_xid (win);
+ ev.xclient.message_type = wm_move_resize_atom;
+ ev.xclient.format = 32;
+
+ ev.xclient.data.l[0] = xevent->xbutton.x_root;
+ ev.xclient.data.l[1] = xevent->xbutton.y_root;
+ ev.xclient.data.l[2] = direction;
+ ev.xclient.data.l[3] = xevent->xbutton.button;
+ ev.xclient.data.l[4] = 1;
+
+ XUngrabPointer (xdisplay, xevent->xbutton.time);
+ XUngrabKeyboard (xdisplay, xevent->xbutton.time);
+
+ XSendEvent (xdisplay, xroot, FALSE,
+ SubstructureRedirectMask | SubstructureNotifyMask,
+ &ev);
+
+ XSync (xdisplay, FALSE);
+}
+
+/* stolen from gtktooltip.c */
+
+#define DEFAULT_DELAY 500 /* Default delay in ms */
+#define STICKY_DELAY 0 /* Delay before popping up next tip
+ * if we're sticky
+ */
+#define STICKY_REVERT_DELAY 1000 /* Delay before sticky tooltips revert
+ * to normal
+ */
+
+static void
+show_tooltip (const char *text)
+{
+ GdkDisplay *gdkdisplay;
+ GtkRequisition requisition;
+ gint x, y, w, h;
+ GdkScreen *screen;
+ gint monitor_num;
+ GdkRectangle monitor;
+
+ gdkdisplay = gdk_display_get_default ();
+
+ gtk_label_set_text (GTK_LABEL (tip_label), text);
+
+ gtk_widget_size_request (tip_window, &requisition);
+
+ w = requisition.width;
+ h = requisition.height;
+
+ gdk_display_get_pointer (gdkdisplay, &screen, &x, &y, NULL);
+
+ x -= (w / 2 + 4);
+
+ monitor_num = gdk_screen_get_monitor_at_point (screen, x, y);
+ gdk_screen_get_monitor_geometry (screen, monitor_num, &monitor);
+
+ if ((x + w) > monitor.x + monitor.width)
+ x -= (x + w) - (monitor.x + monitor.width);
+ else if (x < monitor.x)
+ x = monitor.x;
+
+ if ((y + h + 16) > monitor.y + monitor.height)
+ y = y - h - 16;
+ else
+ y = y + 16;
+
+ gtk_window_move (GTK_WINDOW (tip_window), x, y);
+ gtk_widget_show (tip_window);
+}
+
+static void
+hide_tooltip (void)
+{
+ if (GTK_WIDGET_VISIBLE (tip_window))
+ g_get_current_time (&tooltip_last_popdown);
+
+ gtk_widget_hide (tip_window);
+
+ if (tooltip_timer_tag)
+ {
+ g_source_remove (tooltip_timer_tag);
+ tooltip_timer_tag = 0;
+ }
+}
+
+static gboolean
+tooltip_recently_shown (void)
+{
+ GTimeVal now;
+ glong msec;
+
+ g_get_current_time (&now);
+
+ msec = (now.tv_sec - tooltip_last_popdown.tv_sec) * 1000 +
+ (now.tv_usec - tooltip_last_popdown.tv_usec) / 1000;
+
+ return (msec < STICKY_REVERT_DELAY);
+}
+
+static gint
+tooltip_timeout (gpointer data)
+{
+ tooltip_timer_tag = 0;
+
+ show_tooltip ((const char *) data);
+
+ return FALSE;
+}
+
+static void
+tooltip_start_delay (const char *text)
+{
+ guint delay = DEFAULT_DELAY;
+
+ if (tooltip_timer_tag)
+ return;
+
+ if (tooltip_recently_shown ())
+ delay = STICKY_DELAY;
+
+ tooltip_timer_tag = g_timeout_add (delay,
+ tooltip_timeout,
+ (gpointer) text);
+}
+
+static gint
+tooltip_paint_window (GtkWidget *tooltip)
+{
+ GtkRequisition req;
+
+ gtk_widget_size_request (tip_window, &req);
+ gtk_paint_flat_box (tip_window->style, tip_window->window,
+ GTK_STATE_NORMAL, GTK_SHADOW_OUT,
+ NULL, GTK_WIDGET (tip_window), "tooltip",
+ 0, 0, req.width, req.height);
+
+ return FALSE;
+}
+
+static gboolean
+create_tooltip_window (void)
+{
+ tip_window = gtk_window_new (GTK_WINDOW_POPUP);
+
+ gtk_widget_set_app_paintable (tip_window, TRUE);
+ gtk_window_set_resizable (GTK_WINDOW (tip_window), FALSE);
+ gtk_widget_set_name (tip_window, "gtk-tooltips");
+ gtk_container_set_border_width (GTK_CONTAINER (tip_window), 4);
+
+ g_signal_connect_swapped (tip_window,
+ "expose_event",
+ G_CALLBACK (tooltip_paint_window),
+ 0);
+
+ tip_label = gtk_label_new (NULL);
+ gtk_label_set_line_wrap (GTK_LABEL (tip_label), TRUE);
+ gtk_misc_set_alignment (GTK_MISC (tip_label), 0.5, 0.5);
+ gtk_widget_show (tip_label);
+
+ gtk_container_add (GTK_CONTAINER (tip_window), tip_label);
+
+ gtk_widget_ensure_style (tip_window);
+
+ return TRUE;
+}
+
+static void
+handle_tooltip_event (WnckWindow *win,
+ XEvent *xevent,
+ guint state,
+ const char *tip)
+{
+ switch (xevent->type) {
+ case ButtonPress:
+ hide_tooltip ();
+ break;
+ case ButtonRelease:
+ break;
+ case EnterNotify:
+ if (!(state & PRESSED_EVENT_WINDOW))
+ {
+ if (wnck_window_is_active (win))
+ tooltip_start_delay (tip);
+ }
+ break;
+ case LeaveNotify:
+ if (xevent->xcrossing.mode != NotifyGrab)
+ hide_tooltip ();
+ break;
+ }
+}
+
+static void
+close_button_event (WnckWindow *win,
+ XEvent *xevent)
+{
+ decor_t *d = g_object_get_data (G_OBJECT (win), "decor");
+ guint state = d->button_states[0];
+
+ handle_tooltip_event (win, xevent, state, "Close Window");
+
+ switch (xevent->type) {
+ case ButtonPress:
+ d->button_states[0] |= PRESSED_EVENT_WINDOW;
+ break;
+ case ButtonRelease:
+ if (d->button_states[0] == (PRESSED_EVENT_WINDOW | IN_EVENT_WINDOW))
+ wnck_window_close (win, xevent->xbutton.time);
+
+ d->button_states[0] &= ~PRESSED_EVENT_WINDOW;
+ break;
+ case EnterNotify:
+ d->button_states[0] |= IN_EVENT_WINDOW;
+ break;
+ case LeaveNotify:
+ if (xevent->xcrossing.mode != NotifyGrab)
+ d->button_states[0] &= ~IN_EVENT_WINDOW;
+ break;
+ }
+
+ if (state != d->button_states[0])
+ queue_decor_draw (d);
+}
+
+static void
+max_button_event (WnckWindow *win,
+ XEvent *xevent)
+{
+ decor_t *d = g_object_get_data (G_OBJECT (win), "decor");
+ guint state = d->button_states[1];
+
+ if (wnck_window_is_maximized (win))
+ handle_tooltip_event (win, xevent, state, "Unmaximize Window");
+ else
+ handle_tooltip_event (win, xevent, state, "Maximize Window");
+
+ switch (xevent->type) {
+ case ButtonPress:
+ d->button_states[1] |= PRESSED_EVENT_WINDOW;
+ break;
+ case ButtonRelease:
+ if (d->button_states[1] == (PRESSED_EVENT_WINDOW | IN_EVENT_WINDOW))
+ {
+ if (wnck_window_is_maximized (win))
+ wnck_window_unmaximize (win);
+ else
+ wnck_window_maximize (win);
+ }
+
+ d->button_states[1] &= ~PRESSED_EVENT_WINDOW;
+ break;
+ case EnterNotify:
+ d->button_states[1] |= IN_EVENT_WINDOW;
+ break;
+ case LeaveNotify:
+ if (xevent->xcrossing.mode != NotifyGrab)
+ d->button_states[1] &= ~IN_EVENT_WINDOW;
+ break;
+ }
+
+ if (state != d->button_states[1])
+ queue_decor_draw (d);
+}
+
+static void
+min_button_event (WnckWindow *win,
+ XEvent *xevent)
+{
+ decor_t *d = g_object_get_data (G_OBJECT (win), "decor");
+ guint state = d->button_states[2];
+
+ handle_tooltip_event (win, xevent, state, "Minimize Window");
+
+ switch (xevent->type) {
+ case ButtonPress:
+ d->button_states[2] |= PRESSED_EVENT_WINDOW;
+ break;
+ case ButtonRelease:
+ if (d->button_states[2] == (PRESSED_EVENT_WINDOW | IN_EVENT_WINDOW))
+ wnck_window_minimize (win);
+
+ d->button_states[2] &= ~PRESSED_EVENT_WINDOW;
+ break;
+ case EnterNotify:
+ d->button_states[2] |= IN_EVENT_WINDOW;
+ if (wnck_window_is_active (win))
+ tooltip_start_delay ("Minimize Window");
+ break;
+ case LeaveNotify:
+ if (xevent->xcrossing.mode != NotifyGrab)
+ d->button_states[2] &= ~IN_EVENT_WINDOW;
+ break;
+ }
+
+ if (state != d->button_states[2])
+ queue_decor_draw (d);
+}
+
+static void
+top_left_event (WnckWindow *win,
+ XEvent *xevent)
+{
+ if (xevent->xbutton.button == 1)
+ move_resize_window (win, WM_MOVERESIZE_SIZE_TOPLEFT, xevent);
+}
+
+static void
+top_event (WnckWindow *win,
+ XEvent *xevent)
+{
+ if (xevent->xbutton.button == 1)
+ move_resize_window (win, WM_MOVERESIZE_SIZE_TOP, xevent);
+}
+
+static void
+top_right_event (WnckWindow *win,
+ XEvent *xevent)
+{
+ if (xevent->xbutton.button == 1)
+ move_resize_window (win, WM_MOVERESIZE_SIZE_TOPRIGHT, xevent);
+}
+
+static void
+left_event (WnckWindow *win,
+ XEvent *xevent)
+{
+ if (xevent->xbutton.button == 1)
+ move_resize_window (win, WM_MOVERESIZE_SIZE_LEFT, xevent);
+}
+
+static void
+action_menu_unmap (GObject *object)
+{
+ action_menu_mapped = FALSE;
+}
+
+static void
+title_event (WnckWindow *win,
+ XEvent *xevent)
+{
+ static int last_button_num = 0;
+ static Window last_button_xwindow = None;
+ static Time last_button_time = 0;
+
+ if (xevent->type != ButtonPress)
+ return;
+
+ if (xevent->xbutton.button == 1)
+ {
+ if (xevent->xbutton.button == last_button_num &&
+ xevent->xbutton.window == last_button_xwindow &&
+ xevent->xbutton.time < last_button_time + double_click_timeout)
+ {
+ if (wnck_window_is_maximized (win))
+ wnck_window_unmaximize (win);
+ else
+ wnck_window_maximize (win);
+
+ last_button_num = 0;
+ last_button_xwindow = None;
+ last_button_time = 0;
+ }
+ else
+ {
+ last_button_num = xevent->xbutton.button;
+ last_button_xwindow = xevent->xbutton.window;
+ last_button_time = xevent->xbutton.time;
+
+ move_resize_window (win, WM_MOVERESIZE_MOVE, xevent);
+ }
+ }
+ else if (xevent->xbutton.button == 3)
+ {
+ GdkDisplay *gdkdisplay;
+ GdkScreen *screen;
+
+ gdkdisplay = gdk_display_get_default ();
+ screen = gdk_display_get_default_screen (gdkdisplay);
+
+ if (action_menu)
+ gtk_object_destroy (GTK_OBJECT (action_menu));
+
+ action_menu = wnck_create_window_action_menu (win);
+
+ gtk_menu_set_screen (GTK_MENU (action_menu), screen);
+
+ g_signal_connect_object (G_OBJECT (action_menu), "unmap",
+ G_CALLBACK (action_menu_unmap),
+ 0, 0);
+
+ gtk_widget_show (action_menu);
+ gtk_menu_popup (GTK_MENU (action_menu),
+ NULL, NULL,
+ NULL, NULL,
+ xevent->xbutton.button,
+ xevent->xbutton.time);
+
+ action_menu_mapped = TRUE;
+ }
+}
+
+static void
+right_event (WnckWindow *win,
+ XEvent *xevent)
+{
+ if (xevent->xbutton.button == 1)
+ move_resize_window (win, WM_MOVERESIZE_SIZE_RIGHT, xevent);
+}
+
+static void
+bottom_left_event (WnckWindow *win,
+ XEvent *xevent)
+{
+ if (xevent->xbutton.button == 1)
+ move_resize_window (win, WM_MOVERESIZE_SIZE_BOTTOMLEFT, xevent);
+}
+
+static void
+bottom_event (WnckWindow *win,
+ XEvent *xevent)
+{
+ if (xevent->xbutton.button == 1)
+ move_resize_window (win, WM_MOVERESIZE_SIZE_BOTTOM, xevent);
+}
+
+static void
+bottom_right_event (WnckWindow *win,
+ XEvent *xevent)
+{
+ if (xevent->xbutton.button == 1)
+ move_resize_window (win, WM_MOVERESIZE_SIZE_BOTTOMRIGHT, xevent);
+}
+
+static GdkFilterReturn
+event_filter_func (GdkXEvent *gdkxevent,
+ GdkEvent *event,
+ gpointer data)
+{
+ Display *xdisplay;
+ GdkDisplay *gdkdisplay;
+ XEvent *xevent = gdkxevent;
+ gulong xid = 0;
+
+ gdkdisplay = gdk_display_get_default ();
+ xdisplay = GDK_DISPLAY_XDISPLAY (gdkdisplay);
+
+ switch (xevent->type) {
+ case ButtonPress:
+ case ButtonRelease:
+ xid = (gulong)
+ g_hash_table_lookup (frame_table,
+ GINT_TO_POINTER (xevent->xbutton.window));
+ break;
+ case EnterNotify:
+ case LeaveNotify:
+ xid = (gulong)
+ g_hash_table_lookup (frame_table,
+ GINT_TO_POINTER (xevent->xcrossing.window));
+ break;
+ case MotionNotify:
+ xid = (gulong)
+ g_hash_table_lookup (frame_table,
+ GINT_TO_POINTER (xevent->xmotion.window));
+ break;
+ case PropertyNotify:
+ if (xevent->xproperty.atom == frame_window_atom)
+ {
+ WnckWindow *win;
+
+ xid = xevent->xproperty.window;
+
+ win = wnck_window_get (xid);
+ if (win)
+ {
+ Window frame;
+
+ if (get_window_prop (xid, frame_window_atom, &frame))
+ add_frame_window (win, frame);
+ else
+ remove_frame_window (win);
+ }
+ }
+ break;
+ case DestroyNotify:
+ g_hash_table_remove (frame_table,
+ GINT_TO_POINTER (xevent->xproperty.window));
+ default:
+ break;
+ }
+
+ if (xid)
+ {
+ WnckWindow *win;
+
+ win = wnck_window_get (xid);
+ if (win)
+ {
+ static event_callback callback[3][3] = {
+ { top_left_event, top_event, top_right_event },
+ { left_event, title_event, right_event },
+ { bottom_left_event, bottom_event, bottom_right_event }
+ };
+ static event_callback button_callback[3] = {
+ close_button_event,
+ max_button_event,
+ min_button_event
+ };
+ decor_t *d = g_object_get_data (G_OBJECT (win), "decor");
+
+ if (d->decorated)
+ {
+ gint i, j;
+
+ for (i = 0; i < 3; i++)
+ for (j = 0; j < 3; j++)
+ if (d->event_windows[i][j] == xevent->xany.window)
+ (*callback[i][j]) (win, xevent);
+
+ for (i = 0; i < 3; i++)
+ if (d->button_windows[i] == xevent->xany.window)
+ (*button_callback[i]) (win, xevent);
+ }
+ }
+ }
+
+ return GDK_FILTER_CONTINUE;
+}
+
+/* from clearlooks theme */
+static void
+rgb_to_hls (gdouble *r,
+ gdouble *g,
+ gdouble *b)
+{
+ gdouble min;
+ gdouble max;
+ gdouble red;
+ gdouble green;
+ gdouble blue;
+ gdouble h, l, s;
+ gdouble delta;
+
+ red = *r;
+ green = *g;
+ blue = *b;
+
+ if (red > green)
+ {
+ if (red > blue)
+ max = red;
+ else
+ max = blue;
+
+ if (green < blue)
+ min = green;
+ else
+ min = blue;
+ }
+ else
+ {
+ if (green > blue)
+ max = green;
+ else
+ max = blue;
+
+ if (red < blue)
+ min = red;
+ else
+ min = blue;
+ }
+
+ l = (max + min) / 2;
+ s = 0;
+ h = 0;
+
+ if (max != min)
+ {
+ if (l <= 0.5)
+ s = (max - min) / (max + min);
+ else
+ s = (max - min) / (2 - max - min);
+
+ delta = max -min;
+ if (red == max)
+ h = (green - blue) / delta;
+ else if (green == max)
+ h = 2 + (blue - red) / delta;
+ else if (blue == max)
+ h = 4 + (red - green) / delta;
+
+ h *= 60;
+ if (h < 0.0)
+ h += 360;
+ }
+
+ *r = h;
+ *g = l;
+ *b = s;
+}
+
+static void
+hls_to_rgb (gdouble *h,
+ gdouble *l,
+ gdouble *s)
+{
+ gdouble hue;
+ gdouble lightness;
+ gdouble saturation;
+ gdouble m1, m2;
+ gdouble r, g, b;
+
+ lightness = *l;
+ saturation = *s;
+
+ if (lightness <= 0.5)
+ m2 = lightness * (1 + saturation);
+ else
+ m2 = lightness + saturation - lightness * saturation;
+
+ m1 = 2 * lightness - m2;
+
+ if (saturation == 0)
+ {
+ *h = lightness;
+ *l = lightness;
+ *s = lightness;
+ }
+ else
+ {
+ hue = *h + 120;
+ while (hue > 360)
+ hue -= 360;
+ while (hue < 0)
+ hue += 360;
+
+ if (hue < 60)
+ r = m1 + (m2 - m1) * hue / 60;
+ else if (hue < 180)
+ r = m2;
+ else if (hue < 240)
+ r = m1 + (m2 - m1) * (240 - hue) / 60;
+ else
+ r = m1;
+
+ hue = *h;
+ while (hue > 360)
+ hue -= 360;
+ while (hue < 0)
+ hue += 360;
+
+ if (hue < 60)
+ g = m1 + (m2 - m1) * hue / 60;
+ else if (hue < 180)
+ g = m2;
+ else if (hue < 240)
+ g = m1 + (m2 - m1) * (240 - hue) / 60;
+ else
+ g = m1;
+
+ hue = *h - 120;
+ while (hue > 360)
+ hue -= 360;
+ while (hue < 0)
+ hue += 360;
+
+ if (hue < 60)
+ b = m1 + (m2 - m1) * hue / 60;
+ else if (hue < 180)
+ b = m2;
+ else if (hue < 240)
+ b = m1 + (m2 - m1) * (240 - hue) / 60;
+ else
+ b = m1;
+
+ *h = r;
+ *l = g;
+ *s = b;
+ }
+}
+
+static void
+shade (const decor_color_t *a,
+ decor_color_t *b,
+ float k)
+{
+ double red;
+ double green;
+ double blue;
+
+ red = a->r;
+ green = a->g;
+ blue = a->b;
+
+ rgb_to_hls (&red, &green, &blue);
+
+ green *= k;
+ if (green > 1.0)
+ green = 1.0;
+ else if (green < 0.0)
+ green = 0.0;
+
+ blue *= k;
+ if (blue > 1.0)
+ blue = 1.0;
+ else if (blue < 0.0)
+ blue = 0.0;
+
+ hls_to_rgb (&red, &green, &blue);
+
+ b->r = red;
+ b->g = green;
+ b->b = blue;
+}
+
+static void
+style_changed (GtkWidget *widget)
+{
+ GdkDisplay *gdkdisplay;
+ GdkScreen *gdkscreen;
+ GtkStyle *style;
+ decor_color_t spot_color;
+ WnckScreen *screen;
+ GList *windows;
+
+ gdkdisplay = gdk_display_get_default ();
+ gdkscreen = gdk_display_get_default_screen (gdkdisplay);
+ screen = wnck_screen_get_default ();
+
+ style = gtk_widget_get_style (widget);
+
+ spot_color.r = style->bg[GTK_STATE_SELECTED].red / 65535.0;
+ spot_color.g = style->bg[GTK_STATE_SELECTED].green / 65535.0;
+ spot_color.b = style->bg[GTK_STATE_SELECTED].blue / 65535.0;
+
+ shade (&spot_color, &_title_color[0], 1.05);
+ shade (&_title_color[0], &_title_color[1], 0.85);
+
+ update_default_decorations (gdkscreen);
+
+ windows = wnck_screen_get_windows (screen);
+ while (windows != NULL)
+ {
+ queue_decor_draw (g_object_get_data (G_OBJECT (windows->data),
+ "decor"));
+ windows = windows->next;
+ }
+}
+
+static gboolean
+init_settings (WnckScreen *screen)
+{
+ PangoFontDescription *desc;
+ GtkSettings *settings;
+
+ style_window = gtk_window_new (GTK_WINDOW_POPUP);
+ gtk_widget_ensure_style (style_window);
+
+ style_changed (style_window);
+
+ g_signal_connect_object (style_window, "style-set",
+ G_CALLBACK (style_changed),
+ 0, 0);
+
+ settings = gtk_widget_get_settings (style_window);
+
+ g_object_get (G_OBJECT (settings), "gtk-double-click-time",
+ &double_click_timeout, NULL);
+
+ pango_context = gtk_widget_create_pango_context (style_window);
+
+ desc = pango_font_description_from_string ("Sans Bold 10");
+
+ pango_context_set_font_description (pango_context, desc);
+
+ return TRUE;
+}
+
+int
+main (int argc, char *argv[])
+{
+ GdkDisplay *gdkdisplay;
+ Display *xdisplay;
+ GdkScreen *gdkscreen;
+ WnckScreen *screen;
+ cairo_t *cr;
+ gint i, j;
+
+ gtk_init (&argc, &argv);
+
+ gdkdisplay = gdk_display_get_default ();
+ xdisplay = gdk_x11_display_get_xdisplay (gdkdisplay);
+ gdkscreen = gdk_display_get_default_screen (gdkdisplay);
+
+ frame_window_atom = XInternAtom (xdisplay, "_NET_FRAME_WINDOW", FALSE);
+ win_decor_atom = XInternAtom (xdisplay, "_NET_WINDOW_DECOR", FALSE);
+ win_decor_sync_atom = XInternAtom (xdisplay, "_NET_WINDOW_DECOR_SYNC", FALSE);
+ wm_move_resize_atom = XInternAtom (xdisplay, "_NET_WM_MOVERESIZE", FALSE);
+
+ for (i = 0; i < 3; i++)
+ {
+ for (j = 0; j < 3; j++)
+ {
+ if (cursor[i][j].shape != XC_left_ptr)
+ cursor[i][j].cursor =
+ XCreateFontCursor (xdisplay, cursor[i][j].shape);
+ }
+ }
+
+ frame_table = g_hash_table_new (NULL, NULL);
+
+ if (check_dm_hint (gdkscreen))
+ {
+ fprintf (stderr, "%s: Another window decorator is already running\n",
+ argv[0]);
+ return 1;
+ }
+
+ shadow_pixmap = pixmap_new_from_inline (_shadow);
+ large_shadow_pixmap = pixmap_new_from_inline (_large_shadow);
+
+ if (!shadow_pixmap || !large_shadow_pixmap)
+ {
+ fprintf (stderr, "%s, Failed to load shadow images\n", argv[0]);
+ return 1;
+ }
+
+ cr = gdk_cairo_create (GDK_DRAWABLE (large_shadow_pixmap));
+ shadow_pattern = cairo_pattern_create_for_surface (cairo_get_target (cr));
+ cairo_destroy (cr);
+
+ if (!create_tooltip_window ())
+ {
+ fprintf (stderr, "%s, Couldn't create tooltip window\n", argv[0]);
+ return 1;
+ }
+
+ screen = wnck_screen_get_default ();
+
+ gdk_window_add_filter (NULL,
+ event_filter_func,
+ NULL);
+
+ connect_screen (screen);
+
+ if (!init_settings (screen))
+ {
+ fprintf (stderr, "%s: Failed to get necessary gtk settings\n", argv[0]);
+ return 1;
+ }
+
+ set_dm_check_hint (gdk_display_get_default_screen (gdkdisplay));
+
+ gtk_main ();
+
+ return 0;
+}