/* * Copyright © 2006 Novell, Inc. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * * Author: David Reveman (gwd) * Beryl compatibility fixes and metacity theme restriction by Quinn Storm * Misc fixes by others. * */ #ifdef HAVE_CONFIG_H #include #endif #include #include #include #include #include #include #ifndef GTK_DISABLE_DEPRECATED #define GTK_DISABLE_DEPRECATED #endif #include #include #include #include #define WNCK_I_KNOW_THIS_IS_UNSTABLE #include #include #include #include #if CAIRO_VERSION < CAIRO_VERSION_ENCODE(1, 1, 0) #define CAIRO_EXTEND_PAD CAIRO_EXTEND_NONE #endif #include #include #include #include #include #include #include #include #include #include #define METACITY_GCONF_DIR "/apps/metacity/general" #define COMPIZ_USE_SYSTEM_FONT_KEY \ METACITY_GCONF_DIR "/titlebar_uses_system_font" #define COMPIZ_TITLEBAR_FONT_KEY \ METACITY_GCONF_DIR "/titlebar_font" #define COMPIZ_DOUBLE_CLICK_TITLEBAR_KEY \ METACITY_GCONF_DIR "/action_double_click_titlebar" #define COMPIZ_GCONF_DIR1 "/apps/compiz/plugins/decoration/allscreens/options" #define COMPIZ_SHADOW_RADIUS_KEY \ COMPIZ_GCONF_DIR1 "/shadow_radius" #define COMPIZ_SHADOW_OPACITY_KEY \ COMPIZ_GCONF_DIR1 "/shadow_opacity" #define COMPIZ_SHADOW_COLOR_KEY \ COMPIZ_GCONF_DIR1 "/shadow_color" #define COMPIZ_SHADOW_OFFSET_X_KEY \ COMPIZ_GCONF_DIR1 "/shadow_offset_x" #define COMPIZ_SHADOW_OFFSET_Y_KEY \ COMPIZ_GCONF_DIR1 "/shadow_offset_y" #define META_AUDIBLE_BELL_KEY \ METACITY_GCONF_DIR "/audible_bell" #define META_VISUAL_BELL_KEY \ METACITY_GCONF_DIR "/visual_bell" #define META_VISUAL_BELL_TYPE_KEY \ METACITY_GCONF_DIR "/visual_bell_type" #define META_THEME_KEY \ METACITY_GCONF_DIR "/theme" #define COMPIZ_GCONF_DIR2 "/apps/compiz/general/allscreens/options" #define COMPIZ_AUDIBLE_BELL_KEY \ COMPIZ_GCONF_DIR2 "/audible_bell" #define COMPIZ_GCONF_DIR3 "/apps/compiz/plugins/fade/screen0/options" #define COMPIZ_VISUAL_BELL_KEY \ COMPIZ_GCONF_DIR3 "/visual_bell" #define COMPIZ_FULLSCREEN_VISUAL_BELL_KEY \ COMPIZ_GCONF_DIR3 "/fullscreen_visual_bell" #define GCONF_DIR "/apps/gwd" #define META_THEME_OPACITY_KEY \ GCONF_DIR "/metacity_theme_opacity" #define META_THEME_SHADE_OPACITY_KEY \ GCONF_DIR "/metacity_theme_shade_opacity" #define META_THEME_ACTIVE_OPACITY_KEY \ GCONF_DIR "/metacity_theme_active_opacity" #define META_THEME_ACTIVE_SHADE_OPACITY_KEY \ GCONF_DIR "/metacity_theme_active_shade_opacity" #define STROKE_ALPHA 0.6 #define ICON_SPACE 20 #define DOUBLE_CLICK_DISTANCE 8.0 #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 #define SHADOW_RADIUS 8.0 #define SHADOW_OPACITY 0.5 #define SHADOW_OFFSET_X 1 #define SHADOW_OFFSET_Y 1 #define SHADOW_COLOR_RED 0x0000 #define SHADOW_COLOR_GREEN 0x0000 #define SHADOW_COLOR_BLUE 0x0000 #define META_OPACITY 0.75 #define META_SHADE_OPACITY TRUE #define META_ACTIVE_OPACITY 1.0 #define META_ACTIVE_SHADE_OPACITY TRUE #define MWM_HINTS_DECORATIONS (1L << 1) #define MWM_DECOR_ALL (1L << 0) #define MWM_DECOR_BORDER (1L << 1) #define MWM_DECOR_HANDLE (1L << 2) #define MWM_DECOR_TITLE (1L << 3) #define MWM_DECOR_MENU (1L << 4) #define MWM_DECOR_MINIMIZE (1L << 5) #define MWM_DECOR_MAXIMIZE (1L << 6) #define PROP_MOTIF_WM_HINT_ELEMENTS 3 typedef struct { unsigned long flags; unsigned long functions; unsigned long decorations; } MwmHints; enum { DOUBLE_CLICK_SHADE, DOUBLE_CLICK_MAXIMIZE }; int double_click_action = DOUBLE_CLICK_SHADE; static gboolean minimal = FALSE; static double decoration_alpha = 0.5; #define SWITCHER_SPACE 40 static decor_extents_t _shadow_extents = { 0, 0, 0, 0 }; static decor_extents_t _win_extents = { 6, 6, 4, 6 }; static decor_extents_t _max_win_extents = { 6, 6, 4, 6 }; static decor_extents_t _default_win_extents = { 6, 6, 4, 6 }; static decor_extents_t _switcher_extents = { 6, 6, 6, 6 + SWITCHER_SPACE }; static int titlebar_height = 17; static int max_titlebar_height = 17; static decor_context_t window_context = { { 0, 0, 0, 0 }, 6, 6, 4, 6, 0, 0, 0, 0 }; static decor_context_t max_window_context = { { 0, 0, 0, 0 }, 6, 6, 4, 6, 0, 0, 0, 0 }; static decor_context_t switcher_context = { { 0, 0, 0, 0 }, 6, 6, 6, 6 + SWITCHER_SPACE, 0, 0, 0, 0 }; static decor_context_t shadow_context = { { 0, 0, 0, 0 }, 0, 0, 0, 0, 0, 0, 0, 0, }; static gdouble shadow_radius = SHADOW_RADIUS; static gdouble shadow_opacity = SHADOW_OPACITY; static gushort shadow_color[3] = { SHADOW_COLOR_RED, SHADOW_COLOR_GREEN, SHADOW_COLOR_BLUE }; static gint shadow_offset_x = SHADOW_OFFSET_X; static gint shadow_offset_y = SHADOW_OFFSET_Y; static double meta_opacity = META_OPACITY; static gboolean meta_shade_opacity = META_SHADE_OPACITY; static double meta_active_opacity = META_ACTIVE_OPACITY; static gboolean meta_active_shade_opacity = META_ACTIVE_SHADE_OPACITY; static decor_shadow_t *no_border_shadow = NULL; static decor_shadow_t *border_shadow = NULL; static decor_shadow_t *max_border_shadow = NULL; static decor_shadow_t *switcher_shadow = NULL; static GdkPixmap *decor_normal_pixmap = NULL; static GdkPixmap *decor_active_pixmap = NULL; static Atom frame_window_atom; static Atom win_decor_atom; static Atom wm_move_resize_atom; static Atom restack_window_atom; static Atom select_window_atom; static Atom mwm_hints_atom; static Atom toolkit_action_atom; static Atom toolkit_action_main_menu_atom; static Atom toolkit_action_run_dialog_atom; static Atom toolkit_action_window_menu_atom; static Atom toolkit_action_force_quit_dialog_atom; static Atom panel_action_atom; static Atom panel_action_main_menu_atom; static Atom panel_action_run_dialog_atom; static Time dm_sn_timestamp; #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) } }; 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; decor_layout_t border_layout; decor_context_t *context; decor_shadow_t *shadow; Picture picture; gint button_width; gint width; gint height; gboolean decorated; gboolean active; PangoLayout *layout; gchar *name; cairo_pattern_t *icon; GdkPixmap *icon_pixmap; GdkPixbuf *icon_pixbuf; WnckWindowState state; WnckWindowActions actions; XID prop_xid; GtkWidget *force_quit_dialog; void (*draw) (struct _decor *d); } decor_t; void (*theme_draw_window_decoration) (decor_t *d); gboolean (*theme_calc_decoration_size) (decor_t *d, int client_width, int client_height, int text_width, int *width, int *height); void (*theme_update_border_extents) (gint text_height); void (*theme_get_event_window_position) (decor_t *d, gint i, gint j, gint width, gint height, gint *x, gint *y, gint *w, gint *h); void (*theme_get_button_position) (decor_t *d, gint i, gint width, gint height, gint *x, gint *y, gint *w, gint *h); typedef void (*event_callback) (WnckWindow *win, XEvent *event); static char *program_name; static GtkWidget *style_window; 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 PangoFontDescription *titlebar_font = NULL; static gboolean use_system_font = FALSE; static gint text_height; static GdkPixmap *switcher_pixmap = NULL; static GdkPixmap *switcher_buffer_pixmap = NULL; static gint switcher_width; static gint switcher_height; static XRenderPictFormat *xformat; static void decor_update_switcher_property (decor_t *d) { long data[256]; Display *xdisplay = GDK_DISPLAY_XDISPLAY (gdk_display_get_default ()); gint nQuad; decor_quad_t quads[N_QUADS_MAX]; nQuad = decor_set_lSrStSbX_window_quads (quads, &switcher_context, &d->border_layout, d->border_layout.top.x2 - d->border_layout.top.x1 - switcher_context.extents.left - switcher_context.extents.right - 32); decor_quads_to_property (data, GDK_PIXMAP_XID (d->pixmap), &_switcher_extents, &_switcher_extents, 0, 0, quads, nQuad); gdk_error_trap_push (); XChangeProperty (xdisplay, d->prop_xid, win_decor_atom, XA_INTEGER, 32, PropModeReplace, (guchar *) data, BASE_PROP_SIZE + QUAD_PROP_SIZE * 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); } 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; if (w == 0 || h ==0) abort (); 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; } #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, decor_shadow_t *s, decor_context_t *c) { Display *xdisplay = GDK_DISPLAY_XDISPLAY (gdk_display_get_default ()); if (!s || !d->picture) { cairo_set_source_rgba (cr, 0.0, 0.0, 0.0, 0.0); cairo_paint (cr); } else { decor_fill_picture_extents_with_shadow (xdisplay, s, c, d->picture, &d->border_layout); } } static void decor_update_meta_window_property (decor_t *d, MetaTheme *theme, MetaFrameFlags flags) { long data[256]; Display *xdisplay = GDK_DISPLAY_XDISPLAY (gdk_display_get_default ()); gint nQuad; decor_extents_t extents, max_extents; decor_quad_t quads[N_QUADS_MAX]; gint lh, rh; if (d->border_layout.rotation) lh = d->border_layout.left.x2 - d->border_layout.left.x1; else lh = d->border_layout.left.y2 - d->border_layout.left.y1; if (d->border_layout.rotation) rh = d->border_layout.right.x2 - d->border_layout.right.x1; else rh = d->border_layout.right.y2 - d->border_layout.right.y1; nQuad = decor_set_lXrXtXbX_window_quads (quads, d->context, &d->border_layout, lh / 2, rh / 2, d->border_layout.top.x2 - d->border_layout.top.x1 - d->context->left_space - d->context->right_space - d->button_width - 1, (d->border_layout.bottom.x2 - d->border_layout.bottom.x1 - d->context->left_space - d->context->right_space) / 2); extents = _win_extents; max_extents = _max_win_extents; extents.top += titlebar_height; max_extents.top += max_titlebar_height; decor_quads_to_property (data, GDK_PIXMAP_XID (d->pixmap), &extents, &max_extents, ICON_SPACE + d->button_width, 0, quads, nQuad); gdk_error_trap_push (); XChangeProperty (xdisplay, d->prop_xid, win_decor_atom, XA_INTEGER, 32, PropModeReplace, (guchar *) data, BASE_PROP_SIZE + QUAD_PROP_SIZE * nQuad); XSync (xdisplay, FALSE); gdk_error_trap_pop (); } static void meta_get_corner_radius (const MetaFrameGeometry *fgeom, int *top_left_radius, int *top_right_radius, int *bottom_left_radius, int *bottom_right_radius) { #ifdef HAVE_METACITY_2_17_0 *top_left_radius = fgeom->top_left_corner_rounded_radius; *top_right_radius = fgeom->top_right_corner_rounded_radius; *bottom_left_radius = fgeom->bottom_left_corner_rounded_radius; *bottom_right_radius = fgeom->bottom_right_corner_rounded_radius; #else *top_left_radius = fgeom->top_left_corner_rounded ? 5 : 0; *top_right_radius = fgeom->top_right_corner_rounded ? 5 : 0; *bottom_left_radius = fgeom->bottom_left_corner_rounded ? 5 : 0; *bottom_right_radius = fgeom->bottom_right_corner_rounded ? 5 : 0; #endif } static int radius_to_width (int radius, int i) { int r2 = radius * radius - (radius - i) * (radius - i); return 1 + (radius - floor (sqrt (r2) + 0.5)); } static Region meta_get_top_border_region (const MetaFrameGeometry *fgeom, int width) { Region corners_xregion, border_xregion; XRectangle xrect; int top_left_radius; int top_right_radius; int bottom_left_radius; int bottom_right_radius; int w, i; corners_xregion = XCreateRegion (); meta_get_corner_radius (fgeom, &top_left_radius, &top_right_radius, &bottom_left_radius, &bottom_right_radius); if (top_left_radius) { for (i = 0; i < top_left_radius; i++) { w = radius_to_width (top_left_radius, i); xrect.x = 0; xrect.y = i; xrect.width = w; xrect.height = 1; XUnionRectWithRegion (&xrect, corners_xregion, corners_xregion); } } if (top_right_radius) { for (i = 0; i < top_right_radius; i++) { w = radius_to_width (top_right_radius, i); xrect.x = width - w; xrect.y = i; xrect.width = w; xrect.height = 1; XUnionRectWithRegion (&xrect, corners_xregion, corners_xregion); } } border_xregion = XCreateRegion (); xrect.x = 0; xrect.y = 0; xrect.width = width; xrect.height = fgeom->top_height; XUnionRectWithRegion (&xrect, border_xregion, border_xregion); XSubtractRegion (border_xregion, corners_xregion, border_xregion); XDestroyRegion (corners_xregion); return border_xregion; } static Region meta_get_bottom_border_region (const MetaFrameGeometry *fgeom, int width) { Region corners_xregion, border_xregion; XRectangle xrect; int top_left_radius; int top_right_radius; int bottom_left_radius; int bottom_right_radius; int w, i; corners_xregion = XCreateRegion (); meta_get_corner_radius (fgeom, &top_left_radius, &top_right_radius, &bottom_left_radius, &bottom_right_radius); if (bottom_left_radius) { for (i = 0; i < bottom_left_radius; i++) { w = radius_to_width (bottom_left_radius, i); xrect.x = 0; xrect.y = fgeom->bottom_height - i; xrect.width = w; xrect.height = 1; XUnionRectWithRegion (&xrect, corners_xregion, corners_xregion); } } if (bottom_right_radius) { for (i = 0; i < bottom_right_radius; i++) { w = radius_to_width (bottom_right_radius, i); xrect.x = width - w; xrect.y = fgeom->bottom_height - i; xrect.width = w; xrect.height = 1; XUnionRectWithRegion (&xrect, corners_xregion, corners_xregion); } } border_xregion = XCreateRegion (); xrect.x = 0; xrect.y = 0; xrect.width = width; xrect.height = fgeom->bottom_height; XUnionRectWithRegion (&xrect, border_xregion, border_xregion); XSubtractRegion (border_xregion, corners_xregion, border_xregion); XDestroyRegion (corners_xregion); return border_xregion; } static Region meta_get_left_border_region (const MetaFrameGeometry *fgeom, int height) { Region border_xregion; XRectangle xrect; border_xregion = XCreateRegion (); xrect.x = 0; xrect.y = 0; xrect.width = fgeom->left_width; xrect.height = height - fgeom->top_height - fgeom->bottom_height; XUnionRectWithRegion (&xrect, border_xregion, border_xregion); return border_xregion; } static Region meta_get_right_border_region (const MetaFrameGeometry *fgeom, int height) { Region border_xregion; XRectangle xrect; border_xregion = XCreateRegion (); xrect.x = 0; xrect.y = 0; xrect.width = fgeom->right_width; xrect.height = height - fgeom->top_height - fgeom->bottom_height; XUnionRectWithRegion (&xrect, border_xregion, border_xregion); return border_xregion; } static MetaButtonState meta_button_state (int state) { if (state & IN_EVENT_WINDOW) { if (state & PRESSED_EVENT_WINDOW) return META_BUTTON_STATE_PRESSED; return META_BUTTON_STATE_PRELIGHT; } return META_BUTTON_STATE_NORMAL; } static MetaButtonState meta_button_state_for_button_type (decor_t *d, MetaButtonType type) { switch (type) { case META_BUTTON_TYPE_RIGHT_LEFT_BACKGROUND: case META_BUTTON_TYPE_MINIMIZE: return meta_button_state (d->button_states[2]); case META_BUTTON_TYPE_RIGHT_MIDDLE_BACKGROUND: case META_BUTTON_TYPE_MAXIMIZE: return meta_button_state (d->button_states[1]); case META_BUTTON_TYPE_RIGHT_RIGHT_BACKGROUND: case META_BUTTON_TYPE_CLOSE: return meta_button_state (d->button_states[0]); case META_BUTTON_TYPE_LEFT_LEFT_BACKGROUND: case META_BUTTON_TYPE_LEFT_MIDDLE_BACKGROUND: case META_BUTTON_TYPE_LEFT_RIGHT_BACKGROUND: case META_BUTTON_TYPE_MENU: default: break; } return META_BUTTON_STATE_NORMAL; } static void meta_get_decoration_geometry (decor_t *d, MetaTheme *theme, MetaFrameFlags *flags, MetaFrameGeometry *fgeom, MetaButtonLayout *button_layout, GdkRectangle *clip) { gint left_width, right_width, top_height, bottom_height; gint i; button_layout->left_buttons[0] = META_BUTTON_FUNCTION_MENU; for (i = 1; i < MAX_BUTTONS_PER_CORNER; i++) button_layout->left_buttons[i] = META_BUTTON_FUNCTION_LAST; button_layout->right_buttons[0] = META_BUTTON_FUNCTION_MINIMIZE; button_layout->right_buttons[1] = META_BUTTON_FUNCTION_MAXIMIZE; button_layout->right_buttons[2] = META_BUTTON_FUNCTION_CLOSE; for (i = 3; i < MAX_BUTTONS_PER_CORNER; i++) button_layout->right_buttons[i] = META_BUTTON_FUNCTION_LAST; *flags = 0; if (d->actions & WNCK_WINDOW_ACTION_CLOSE) *flags |= META_FRAME_ALLOWS_DELETE; if (d->actions & WNCK_WINDOW_ACTION_MINIMIZE) *flags |= META_FRAME_ALLOWS_MINIMIZE; if (d->actions & WNCK_WINDOW_ACTION_MAXIMIZE) *flags |= META_FRAME_ALLOWS_MAXIMIZE; *flags |= META_FRAME_ALLOWS_MENU; *flags |= META_FRAME_ALLOWS_VERTICAL_RESIZE; *flags |= META_FRAME_ALLOWS_HORIZONTAL_RESIZE; *flags |= META_FRAME_ALLOWS_MOVE; if (d->actions & WNCK_WINDOW_ACTION_MAXIMIZE) *flags |= META_FRAME_ALLOWS_MAXIMIZE; if (d->active) *flags |= META_FRAME_HAS_FOCUS; #define META_MAXIMIZED (WNCK_WINDOW_STATE_MAXIMIZED_HORIZONTALLY | \ WNCK_WINDOW_STATE_MAXIMIZED_VERTICALLY) if ((d->state & META_MAXIMIZED) == META_MAXIMIZED) *flags |= META_FRAME_MAXIMIZED; meta_theme_get_frame_borders (theme, META_FRAME_TYPE_NORMAL, text_height, *flags, &top_height, &bottom_height, &left_width, &right_width); clip->x = d->context->left_space - left_width; clip->y = d->context->top_space - top_height; clip->width = d->border_layout.top.x2 - d->border_layout.top.x1; clip->width -= d->context->right_space + d->context->left_space; if (d->border_layout.rotation) clip->height = d->border_layout.left.x2 - d->border_layout.left.x1; else clip->height = d->border_layout.left.y2 - d->border_layout.left.y1; meta_theme_calc_geometry (theme, META_FRAME_TYPE_NORMAL, text_height, *flags, clip->width, clip->height, button_layout, fgeom); clip->width += left_width + right_width; clip->height += top_height + bottom_height; } static void meta_draw_window_decoration (decor_t *d) { Display *xdisplay = GDK_DISPLAY_XDISPLAY (gdk_display_get_default ()); GdkPixmap *pixmap; Picture src; MetaButtonState button_states[META_BUTTON_TYPE_LAST]; MetaButtonLayout button_layout; MetaFrameGeometry fgeom; MetaFrameFlags flags; MetaTheme *theme; GtkStyle *style; cairo_t *cr; gint size, i; GdkRectangle clip, rect; GdkDrawable *drawable; Region region; double alpha = (d->active) ? meta_active_opacity : meta_opacity; gboolean shade_alpha = (d->active) ? meta_active_shade_opacity : meta_shade_opacity; MetaFrameStyle *frame_style; GdkColor bg_color; double bg_alpha; if (!d->pixmap || !d->picture) return; if (decoration_alpha == 1.0) alpha = 1.0; style = gtk_widget_get_style (style_window); drawable = d->buffer_pixmap ? d->buffer_pixmap : d->pixmap; cr = gdk_cairo_create (GDK_DRAWABLE (drawable)); cairo_set_operator (cr, CAIRO_OPERATOR_SOURCE); theme = meta_theme_get_current (); meta_get_decoration_geometry (d, theme, &flags, &fgeom, &button_layout, &clip); /* we only have to redraw the shadow background when decoration changed size */ if (d->prop_xid || !d->buffer_pixmap) draw_shadow_background (d, cr, d->shadow, d->context); for (i = 0; i < META_BUTTON_TYPE_LAST; i++) button_states[i] = meta_button_state_for_button_type (d, i); frame_style = meta_theme_get_frame_style (theme, META_FRAME_TYPE_NORMAL, flags); bg_color = style->bg[GTK_STATE_NORMAL]; bg_alpha = 1.0; #ifdef HAVE_METACITY_2_17_0 if (frame_style->window_background_color) { meta_color_spec_render (frame_style->window_background_color, GTK_WIDGET (style_window), &bg_color); bg_alpha = frame_style->window_background_alpha / 255.0; } #endif cairo_destroy (cr); rect.x = 0; rect.y = 0; rect.width = clip.width; size = MAX (fgeom.top_height, fgeom.bottom_height); if (rect.width && size) { pixmap = create_pixmap (rect.width, size); cr = gdk_cairo_create (GDK_DRAWABLE (pixmap)); gdk_cairo_set_source_color_alpha (cr, &bg_color, bg_alpha); cairo_set_operator (cr, CAIRO_OPERATOR_SOURCE); src = XRenderCreatePicture (xdisplay, GDK_PIXMAP_XID (pixmap), xformat, 0, NULL); if (fgeom.top_height) { rect.height = fgeom.top_height; cairo_paint (cr); meta_theme_draw_frame (theme, style_window, pixmap, &rect, 0, 0, META_FRAME_TYPE_NORMAL, flags, clip.width - fgeom.left_width - fgeom.right_width, clip.height - fgeom.top_height - fgeom.bottom_height, d->layout, text_height, &button_layout, button_states, d->icon_pixbuf, NULL); region = meta_get_top_border_region (&fgeom, clip.width); decor_blend_top_border_picture (xdisplay, d->context, src, 0, 0, d->picture, &d->border_layout, region, alpha * 0xffff, shade_alpha); XDestroyRegion (region); } if (fgeom.bottom_height) { rect.height = fgeom.bottom_height; cairo_paint (cr); meta_theme_draw_frame (theme, style_window, pixmap, &rect, 0, -(clip.height - fgeom.bottom_height), META_FRAME_TYPE_NORMAL, flags, clip.width - fgeom.left_width - fgeom.right_width, clip.height - fgeom.top_height - fgeom.bottom_height, d->layout, text_height, &button_layout, button_states, d->icon_pixbuf, NULL); region = meta_get_bottom_border_region (&fgeom, clip.width); decor_blend_bottom_border_picture (xdisplay, d->context, src, 0, 0, d->picture, &d->border_layout, region, alpha * 0xffff, shade_alpha); XDestroyRegion (region); } cairo_destroy (cr); gdk_pixmap_unref (pixmap); XRenderFreePicture (xdisplay, src); } rect.height = clip.height - fgeom.top_height - fgeom.bottom_height; size = MAX (fgeom.left_width, fgeom.right_width); if (size && rect.height) { pixmap = create_pixmap (size, rect.height); cr = gdk_cairo_create (GDK_DRAWABLE (pixmap)); gdk_cairo_set_source_color_alpha (cr, &bg_color, bg_alpha); cairo_set_operator (cr, CAIRO_OPERATOR_SOURCE); src = XRenderCreatePicture (xdisplay, GDK_PIXMAP_XID (pixmap), xformat, 0, NULL); if (fgeom.left_width) { rect.width = fgeom.left_width; cairo_paint (cr); meta_theme_draw_frame (theme, style_window, pixmap, &rect, 0, -fgeom.top_height, META_FRAME_TYPE_NORMAL, flags, clip.width - fgeom.left_width - fgeom.right_width, clip.height - fgeom.top_height - fgeom.bottom_height, d->layout, text_height, &button_layout, button_states, d->icon_pixbuf, NULL); region = meta_get_left_border_region (&fgeom, clip.height); decor_blend_left_border_picture (xdisplay, d->context, src, 0, 0, d->picture, &d->border_layout, region, alpha * 0xffff, shade_alpha); XDestroyRegion (region); } if (fgeom.right_width) { rect.width = fgeom.right_width; cairo_paint (cr); meta_theme_draw_frame (theme, style_window, pixmap, &rect, -(clip.width - fgeom.right_width), -fgeom.top_height, META_FRAME_TYPE_NORMAL, flags, clip.width - fgeom.left_width - fgeom.right_width, clip.height - fgeom.top_height - fgeom.bottom_height, d->layout, text_height, &button_layout, button_states, d->icon_pixbuf, NULL); region = meta_get_right_border_region (&fgeom, clip.height); decor_blend_right_border_picture (xdisplay, d->context, src, 0, 0, d->picture, &d->border_layout, region, alpha * 0xffff, shade_alpha); XDestroyRegion (region); } cairo_destroy (cr); gdk_pixmap_unref (pixmap); XRenderFreePicture (xdisplay, src); } 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_meta_window_property (d, theme, flags); d->prop_xid = 0; } } #define SWITCHER_ALPHA 0xa0a0 static void draw_switcher_background (decor_t *d) { Display *xdisplay = GDK_DISPLAY_XDISPLAY (gdk_display_get_default ()); cairo_t *cr; GtkStyle *style; decor_color_t color; double alpha = SWITCHER_ALPHA / 65535.0; double x1, y1, x2, y2, h; int top; unsigned long pixel; ushort a = SWITCHER_ALPHA; if (!d->buffer_pixmap) return; style = gtk_widget_get_style (style_window); 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; cr = gdk_cairo_create (GDK_DRAWABLE (d->buffer_pixmap)); cairo_set_operator (cr, CAIRO_OPERATOR_SOURCE); top = _switcher_extents.top; x1 = switcher_context.left_space - _switcher_extents.left; y1 = switcher_context.top_space - _switcher_extents.top; x2 = d->width - switcher_context.right_space + _switcher_extents.right; y2 = d->height - switcher_context.bottom_space + _switcher_extents.bottom; h = y2 - y1 - _switcher_extents.top - _switcher_extents.top; cairo_set_line_width (cr, 1.0); cairo_set_operator (cr, CAIRO_OPERATOR_SOURCE); draw_shadow_background (d, cr, switcher_shadow, &switcher_context); fill_rounded_rectangle (cr, x1 + 0.5, y1 + 0.5, _switcher_extents.left - 0.5, top - 0.5, 5.0, CORNER_TOPLEFT, &color, alpha, &color, alpha * 0.75, SHADE_TOP | SHADE_LEFT); fill_rounded_rectangle (cr, x1 + _switcher_extents.left, y1 + 0.5, x2 - x1 - _switcher_extents.left - _switcher_extents.right, top - 0.5, 5.0, 0, &color, alpha, &color, alpha * 0.75, SHADE_TOP); fill_rounded_rectangle (cr, x2 - _switcher_extents.right, y1 + 0.5, _switcher_extents.right - 0.5, top - 0.5, 5.0, CORNER_TOPRIGHT, &color, alpha, &color, alpha * 0.75, SHADE_TOP | SHADE_RIGHT); fill_rounded_rectangle (cr, x1 + 0.5, y1 + top, _switcher_extents.left - 0.5, h, 5.0, 0, &color, alpha, &color, alpha * 0.75, SHADE_LEFT); fill_rounded_rectangle (cr, x2 - _switcher_extents.right, y1 + top, _switcher_extents.right - 0.5, h, 5.0, 0, &color, alpha, &color, alpha * 0.75, SHADE_RIGHT); fill_rounded_rectangle (cr, x1 + 0.5, y2 - _switcher_extents.top, _switcher_extents.left - 0.5, _switcher_extents.top - 0.5, 5.0, CORNER_BOTTOMLEFT, &color, alpha, &color, alpha * 0.75, SHADE_BOTTOM | SHADE_LEFT); fill_rounded_rectangle (cr, x1 + _switcher_extents.left, y2 - _switcher_extents.top, x2 - x1 - _switcher_extents.left - _switcher_extents.right, _switcher_extents.top - 0.5, 5.0, 0, &color, alpha, &color, alpha * 0.75, SHADE_BOTTOM); fill_rounded_rectangle (cr, x2 - _switcher_extents.right, y2 - _switcher_extents.top, _switcher_extents.right - 0.5, _switcher_extents.top - 0.5, 5.0, CORNER_BOTTOMRIGHT, &color, alpha, &color, alpha * 0.75, SHADE_BOTTOM | SHADE_RIGHT); cairo_rectangle (cr, x1 + _switcher_extents.left, y1 + top, x2 - x1 - _switcher_extents.left - _switcher_extents.right, h); gdk_cairo_set_source_color_alpha (cr, &style->bg[GTK_STATE_NORMAL], alpha); cairo_fill (cr); cairo_set_operator (cr, CAIRO_OPERATOR_OVER); 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); 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); 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); 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); gdk_cairo_set_source_color_alpha (cr, &style->fg[GTK_STATE_NORMAL], alpha); cairo_stroke (cr); cairo_destroy (cr); gdk_draw_drawable (d->pixmap, d->gc, d->buffer_pixmap, 0, 0, 0, 0, d->width, d->height); pixel = ((((a * style->bg[GTK_STATE_NORMAL].red ) >> 24) & 0x0000ff) | (((a * style->bg[GTK_STATE_NORMAL].green) >> 16) & 0x00ff00) | (((a * style->bg[GTK_STATE_NORMAL].blue ) >> 8) & 0xff0000) | (((a & 0xff00) << 16))); decor_update_switcher_property (d); gdk_error_trap_push (); XSetWindowBackground (xdisplay, d->prop_xid, pixel); XClearWindow (xdisplay, d->prop_xid); XSync (xdisplay, FALSE); gdk_error_trap_pop (); d->prop_xid = 0; } static void draw_switcher_foreground (decor_t *d) { cairo_t *cr; GtkStyle *style; decor_color_t color; double alpha = SWITCHER_ALPHA / 65535.0; if (!d->pixmap || !d->buffer_pixmap) return; style = gtk_widget_get_style (style_window); 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; cr = gdk_cairo_create (GDK_DRAWABLE (d->buffer_pixmap)); cairo_set_operator (cr, CAIRO_OPERATOR_SOURCE); cairo_rectangle (cr, switcher_context.left_space, d->height - switcher_context.bottom_space, d->width - switcher_context.left_space - switcher_context.right_space, SWITCHER_SPACE); gdk_cairo_set_source_color_alpha (cr, &style->bg[GTK_STATE_NORMAL], alpha); cairo_fill (cr); if (d->layout) { int w; cairo_set_operator (cr, CAIRO_OPERATOR_OVER); gdk_cairo_set_source_color_alpha (cr, &style->fg[GTK_STATE_NORMAL], 1.0); pango_layout_get_pixel_size (d->layout, &w, NULL); cairo_move_to (cr, d->width / 2 - w / 2, d->height - switcher_context.bottom_space + SWITCHER_SPACE / 2 - text_height / 2); pango_cairo_show_layout (cr, d->layout); } cairo_destroy (cr); gdk_draw_drawable (d->pixmap, d->gc, d->buffer_pixmap, 0, 0, 0, 0, d->width, d->height); } static void draw_switcher_decoration (decor_t *d) { if (d->prop_xid) draw_switcher_background (d); draw_switcher_foreground (d); } static gboolean draw_decor_list (void *data) { GSList *list; decor_t *d; draw_idle_id = 0; for (list = draw_list; list; list = list->next) { d = (decor_t *) list->data; (*d->draw) (d); } 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 * 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 void update_default_decorations (GdkScreen *screen) { long data[256]; Window xroot; GdkDisplay *gdkdisplay = gdk_display_get_default (); Display *xdisplay = gdk_x11_display_get_xdisplay (gdkdisplay); Atom bareAtom, normalAtom, activeAtom; decor_t d; gint nQuad; decor_quad_t quads[N_QUADS_MAX]; decor_extents_t extents = _win_extents; xroot = RootWindowOfScreen (gdk_x11_screen_get_xscreen (screen)); bareAtom = XInternAtom (xdisplay, "_NET_WINDOW_DECOR_BARE", FALSE); normalAtom = XInternAtom (xdisplay, "_NET_WINDOW_DECOR_NORMAL", FALSE); activeAtom = XInternAtom (xdisplay, "_NET_WINDOW_DECOR_ACTIVE", FALSE); if (no_border_shadow) { decor_layout_t layout; decor_get_default_layout (&shadow_context, 1, 1, &layout); nQuad = decor_set_lSrStSbS_window_quads (quads, &shadow_context, &layout); decor_quads_to_property (data, no_border_shadow->pixmap, &_shadow_extents, &_shadow_extents, 0, 0, quads, nQuad); XChangeProperty (xdisplay, xroot, bareAtom, XA_INTEGER, 32, PropModeReplace, (guchar *) data, BASE_PROP_SIZE + QUAD_PROP_SIZE * nQuad); if (minimal) { XChangeProperty (xdisplay, xroot, normalAtom, XA_INTEGER, 32, PropModeReplace, (guchar *) data, BASE_PROP_SIZE + QUAD_PROP_SIZE * nQuad); XChangeProperty (xdisplay, xroot, activeAtom, XA_INTEGER, 32, PropModeReplace, (guchar *) data, BASE_PROP_SIZE + QUAD_PROP_SIZE * nQuad); } } else { XDeleteProperty (xdisplay, xroot, bareAtom); if (minimal) { XDeleteProperty (xdisplay, xroot, normalAtom); XDeleteProperty (xdisplay, xroot, activeAtom); } } if (minimal) return; memset (&d, 0, sizeof (d)); d.context = &window_context; d.shadow = border_shadow; decor_get_default_layout (d.context, 1, 1, &d.border_layout); d.width = d.border_layout.width; d.height = d.border_layout.height; extents.top += titlebar_height; d.draw = theme_draw_window_decoration; if (decor_normal_pixmap) gdk_pixmap_unref (decor_normal_pixmap); nQuad = decor_set_lSrStSbS_window_quads (quads, d.context, &d.border_layout); decor_normal_pixmap = create_pixmap (d.width, d.height); if (decor_normal_pixmap) { d.pixmap = decor_normal_pixmap; d.active = FALSE; d.picture = XRenderCreatePicture (xdisplay, GDK_PIXMAP_XID (d.pixmap), xformat, 0, NULL); (*d.draw) (&d); XRenderFreePicture (xdisplay, d.picture); decor_quads_to_property (data, GDK_PIXMAP_XID (d.pixmap), &extents, &extents, 0, 0, quads, nQuad); XChangeProperty (xdisplay, xroot, normalAtom, XA_INTEGER, 32, PropModeReplace, (guchar *) data, BASE_PROP_SIZE + QUAD_PROP_SIZE * nQuad); } if (decor_active_pixmap) gdk_pixmap_unref (decor_active_pixmap); decor_active_pixmap = create_pixmap (d.width, d.height); if (decor_active_pixmap) { d.pixmap = decor_active_pixmap; d.active = TRUE; d.picture = XRenderCreatePicture (xdisplay, GDK_PIXMAP_XID (d.pixmap), xformat, 0, NULL); (*d.draw) (&d); XRenderFreePicture (xdisplay, d.picture); decor_quads_to_property (data, GDK_PIXMAP_XID (d.pixmap), &extents, &extents, 0, 0, quads, nQuad); XChangeProperty (xdisplay, xroot, activeAtom, XA_INTEGER, 32, PropModeReplace, (guchar *) data, BASE_PROP_SIZE + QUAD_PROP_SIZE * nQuad); } } 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 unsigned int get_mwm_prop (Window xwindow) { Display *xdisplay; Atom actual; int err, result, format; unsigned long n, left; MwmHints *mwm_hints; unsigned int decor = MWM_DECOR_ALL; xdisplay = GDK_DISPLAY_XDISPLAY (gdk_display_get_default ()); gdk_error_trap_push (); result = XGetWindowProperty (xdisplay, xwindow, mwm_hints_atom, 0L, 20L, FALSE, mwm_hints_atom, &actual, &format, &n, &left, (unsigned char **) &mwm_hints); err = gdk_error_trap_pop (); if (err != Success || result != Success) return decor; if (n && mwm_hints) { if (n >= PROP_MOTIF_WM_HINT_ELEMENTS) { if (mwm_hints->flags & MWM_HINTS_DECORATIONS) decor = mwm_hints->decorations; } XFree (mwm_hints); } return decor; } #define TOP_RESIZE_HEIGHT 2 static void meta_get_event_window_position (decor_t *d, gint i, gint j, gint width, gint height, gint *x, gint *y, gint *w, gint *h) { MetaButtonLayout button_layout; MetaFrameGeometry fgeom; MetaFrameFlags flags; MetaTheme *theme; GdkRectangle clip; theme = meta_theme_get_current (); meta_get_decoration_geometry (d, theme, &flags, &fgeom, &button_layout, &clip); width += fgeom.right_width + fgeom.left_width; height += fgeom.top_height + fgeom.bottom_height; switch (i) { case 2: /* bottom */ switch (j) { case 2: /* bottom right */ *x = width - fgeom.right_width; *y = height - fgeom.bottom_height; *w = fgeom.right_width; *h = fgeom.bottom_height; break; case 1: /* bottom */ *x = fgeom.left_width; *y = height - fgeom.bottom_height; *w = width - fgeom.left_width - fgeom.right_width; *h = fgeom.bottom_height; break; case 0: /* bottom left */ default: *x = 0; *y = height - fgeom.bottom_height; *w = fgeom.left_width; *h = fgeom.bottom_height; break; } break; case 1: /* middle */ switch (j) { case 2: /* right */ *x = width - fgeom.right_width; *y = fgeom.top_height; *w = fgeom.right_width; *h = height - fgeom.top_height - fgeom.bottom_height; break; case 1: /* middle */ *x = fgeom.left_width; *y = fgeom.title_rect.y + TOP_RESIZE_HEIGHT; *w = width - fgeom.left_width - fgeom.right_width; *h = height - fgeom.top_titlebar_edge - fgeom.bottom_height; break; case 0: /* left */ default: *x = 0; *y = fgeom.top_height; *w = fgeom.left_width; *h = height - fgeom.top_height - fgeom.bottom_height; break; } break; case 0: /* top */ default: switch (j) { case 2: /* top right */ *x = width - fgeom.right_width; *y = 0; *w = fgeom.right_width; *h = fgeom.top_height; break; case 1: /* top */ *x = fgeom.left_width; *y = 0; *w = width - fgeom.left_width - fgeom.right_width; *h = fgeom.title_rect.y + TOP_RESIZE_HEIGHT; break; case 0: /* top left */ default: *x = 0; *y = 0; *w = fgeom.left_width; *h = fgeom.top_height; break; } } } static void meta_get_button_position (decor_t *d, gint i, gint width, gint height, gint *x, gint *y, gint *w, gint *h) { MetaButtonLayout button_layout; MetaFrameGeometry fgeom; MetaFrameFlags flags; MetaTheme *theme; GdkRectangle clip; #ifdef HAVE_METACITY_2_15_21 MetaButtonSpace *space; #else GdkRectangle *space; #endif theme = meta_theme_get_current (); meta_get_decoration_geometry (d, theme, &flags, &fgeom, &button_layout, &clip); switch (i) { case 2: space = &fgeom.min_rect; break; case 1: space = &fgeom.max_rect; break; case 0: default: space = &fgeom.close_rect; break; } #ifdef HAVE_METACITY_2_15_21 *x = space->clickable.x; *y = space->clickable.y; *w = space->clickable.width; *h = space->clickable.height; #else *x = space->x; *y = space->y; *w = space->width; *h = space->height; #endif } 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, k, l; xdisplay = GDK_DISPLAY_XDISPLAY (gdk_display_get_default ()); wnck_window_get_geometry (win, &x0, &y0, &width, &height); if (d->state & WNCK_WINDOW_STATE_SHADED) { height = 0; k = l = 1; } else { k = 0; l = 2; } 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] && i >= k && i <= l) { (*theme_get_event_window_position) (d, i, j, width, height, &x, &y, &w, &h); 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]) { (*theme_get_button_position) (d, i, width, height, &x, &y, &w, &h); 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 (); } #if HAVE_WNCK_WINDOW_HAS_NAME static const char * wnck_window_get_real_name (WnckWindow *win) { return wnck_window_has_name (win) ? wnck_window_get_name (win) : NULL; } #define wnck_window_get_name wnck_window_get_real_name #endif static gint max_window_name_width (WnckWindow *win) { decor_t *d = g_object_get_data (G_OBJECT (win), "decor"); const gchar *name; gint w; if (!d->layout) { d->layout = pango_layout_new (pango_context); if (!d->layout) return 0; pango_layout_set_wrap (d->layout, PANGO_WRAP_CHAR); } name = wnck_window_get_name (win); if (!name) return 0; 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 + 6; } 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; if (theme_draw_window_decoration != meta_draw_window_decoration) { w = SHRT_MAX; } else { int width; wnck_window_get_geometry(win,0,0,&width,0); w= width - ICON_SPACE -2 - d->button_width; 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) { pango_layout_set_text (d->layout, NULL, 0); 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); } } static void update_window_decoration_icon (WnckWindow *win) { decor_t *d = g_object_get_data (G_OBJECT (win), "decor"); 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; } if (d->icon_pixbuf) gdk_pixbuf_unref (d->icon_pixbuf); d->icon_pixbuf = wnck_window_get_mini_icon (win); if (d->icon_pixbuf) { cairo_t *cr; gdk_pixbuf_ref (d->icon_pixbuf); d->icon_pixmap = pixmap_new_from_pixbuf (d->icon_pixbuf); 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 void meta_calc_button_size (decor_t *d) { gint i, min_x, x, y, w, h, width; width = d->border_layout.top.x2 - d->border_layout.top.x1 - d->context->left_space - d->context->right_space; min_x = width; 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]) { meta_get_button_position (d, i, width, 256, &x, &y, &w, &h); if (x < min_x) min_x = x; } } d->button_width = width - min_x; } static gboolean meta_calc_decoration_size (decor_t *d, gint w, gint h, gint name_width, gint *width, gint *height) { decor_layout_t layout; decor_context_t *context; decor_shadow_t *shadow; if ((d->state & META_MAXIMIZED) == META_MAXIMIZED) { context = &max_window_context; shadow = max_border_shadow; } else { context = &window_context; shadow = border_shadow; } decor_get_best_layout (context, w, h, &layout); if (context != d->context || memcmp (&layout, &d->border_layout, sizeof (layout))) { *width = layout.width; *height = layout.height; d->border_layout = layout; d->context = context; d->shadow = shadow; meta_calc_button_size (d); return TRUE; } return FALSE; } static gboolean update_window_decoration_size (WnckWindow *win) { decor_t *d = g_object_get_data (G_OBJECT (win), "decor"); GdkPixmap *pixmap, *buffer_pixmap = NULL; Picture picture; gint width, height; gint w, h, name_width; Display *xdisplay; xdisplay = GDK_DISPLAY_XDISPLAY (gdk_display_get_default ()); wnck_window_get_geometry (win, NULL, NULL, &w, &h); name_width = max_window_name_width (win); if (!(*theme_calc_decoration_size) (d, w, h, name_width, &width, &height)) { update_window_decoration_name (win); return FALSE; } pixmap = create_pixmap (width, height); if (!pixmap) return FALSE; buffer_pixmap = create_pixmap (width, height); if (!buffer_pixmap) { gdk_pixmap_unref (pixmap); return FALSE; } picture = XRenderCreatePicture (xdisplay, GDK_PIXMAP_XID (buffer_pixmap), xformat, 0, NULL); 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); if (d->picture) XRenderFreePicture (xdisplay, d->picture); d->pixmap = pixmap; d->buffer_pixmap = buffer_pixmap; d->gc = gdk_gc_new (pixmap); d->picture = picture; 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 ()) { if (get_mwm_prop (xid) & (MWM_DECOR_ALL | MWM_DECOR_TITLE)) 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 gboolean update_switcher_window (WnckWindow *win, Window selected) { decor_t *d = g_object_get_data (G_OBJECT (win), "decor"); GdkPixmap *pixmap, *buffer_pixmap = NULL; gint height, width = 0; WnckWindow *selected_win; Display *xdisplay; xdisplay = GDK_DISPLAY_XDISPLAY (gdk_display_get_default ()); wnck_window_get_geometry (win, NULL, NULL, &width, NULL); decor_get_default_layout (&switcher_context, width, 1, &d->border_layout); width = d->border_layout.width; height = d->border_layout.height; d->decorated = FALSE; d->draw = draw_switcher_decoration; if (!d->pixmap && switcher_pixmap) { gdk_pixmap_ref (switcher_pixmap); d->pixmap = switcher_pixmap; } if (!d->buffer_pixmap && switcher_buffer_pixmap) { gdk_pixmap_ref (switcher_buffer_pixmap); d->buffer_pixmap = switcher_buffer_pixmap; } if (!d->width) d->width = switcher_width; if (!d->height) d->height = switcher_height; selected_win = wnck_window_get (selected); if (selected_win) { glong name_length; PangoLayoutLine *line; const gchar *name; if (d->name) { g_free (d->name); d->name = NULL; } name = wnck_window_get_name (selected_win); if (name && (name_length = strlen (name))) { gint n_line; if (!d->layout) { d->layout = pango_layout_new (pango_context); if (d->layout) pango_layout_set_wrap (d->layout, PANGO_WRAP_CHAR); } if (d->layout) { int tw; tw = width - switcher_context.left_space - switcher_context.right_space - 64; pango_layout_set_width (d->layout, tw * 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; } else { d->name = g_strndup (name, name_length); strcpy (d->name + name_length - 3, "..."); } } else d->name = g_strndup (name, name_length); if (d->layout) pango_layout_set_text (d->layout, d->name, name_length); } } else if (d->layout) { g_object_unref (G_OBJECT (d->layout)); d->layout = NULL; } } if (width == d->width && height == d->height) { if (!d->gc) d->gc = gdk_gc_new (d->pixmap); if (!d->picture) d->picture = XRenderCreatePicture (xdisplay, GDK_PIXMAP_XID (d->buffer_pixmap), xformat, 0, NULL); queue_decor_draw (d); return FALSE; } pixmap = create_pixmap (width, height); if (!pixmap) return FALSE; buffer_pixmap = create_pixmap (width, height); if (!buffer_pixmap) { gdk_pixmap_unref (pixmap); return FALSE; } if (switcher_pixmap) gdk_pixmap_unref (switcher_pixmap); if (switcher_buffer_pixmap) gdk_pixmap_unref (switcher_buffer_pixmap); 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); if (d->picture) XRenderFreePicture (xdisplay, d->picture); switcher_pixmap = pixmap; switcher_buffer_pixmap = buffer_pixmap; switcher_width = width; switcher_height = height; gdk_pixmap_ref (pixmap); gdk_pixmap_ref (buffer_pixmap); d->pixmap = pixmap; d->buffer_pixmap = buffer_pixmap; d->gc = gdk_gc_new (pixmap); d->picture = XRenderCreatePicture (xdisplay, GDK_PIXMAP_XID (buffer_pixmap), xformat, 0, NULL); d->width = width; d->height = height; d->prop_xid = wnck_window_get_xid (win); queue_decor_draw (d); return TRUE; } static void remove_frame_window (WnckWindow *win) { decor_t *d = g_object_get_data (G_OBJECT (win), "decor"); Display *xdisplay; xdisplay = GDK_DISPLAY_XDISPLAY (gdk_display_get_default ()); 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->picture) { XRenderFreePicture (xdisplay, d->picture); d->picture = 0; } 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; } if (d->icon_pixbuf) { gdk_pixbuf_unref (d->icon_pixbuf); d->icon_pixbuf = NULL; } if (d->force_quit_dialog) { GtkWidget *dialog = d->force_quit_dialog; d->force_quit_dialog = NULL; gtk_widget_destroy (dialog); } d->width = 0; d->height = 0; d->decorated = FALSE; d->state = 0; d->actions = 0; d->context = NULL; d->shadow = NULL; 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); if (update_window_decoration_size(win)) update_event_windows (win); else 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); if (update_window_decoration_size (win)) update_event_windows (win); else 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 window; gulong xid; d = calloc (1, sizeof (decor_t)); if (!d) return; d->active = wnck_window_is_active (win); d->draw = theme_draw_window_decoration; g_object_set_data (G_OBJECT (win), "decor", d); connect_window (win); xid = wnck_window_get_xid (win); if (get_window_prop (xid, select_window_atom, &window)) { d->prop_xid = wnck_window_get_xid (win); update_switcher_window (win, window); } else if (get_window_prop (xid, frame_window_atom, &window)) { add_frame_window (win, window); } } static void window_closed (WnckScreen *screen, WnckWindow *win) { Display *xdisplay = GDK_DISPLAY_XDISPLAY (gdk_display_get_default ()); decor_t *d = g_object_get_data (G_OBJECT (win), "decor"); remove_frame_window (win); gdk_error_trap_push (); XDeleteProperty (xdisplay, wnck_window_get_xid (win), win_decor_atom); XSync (xdisplay, FALSE); gdk_error_trap_pop (); 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); } static void restack_window (WnckWindow *win, int stack_mode) { 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 = restack_window_atom; ev.xclient.format = 32; ev.xclient.data.l[0] = 2; ev.xclient.data.l[1] = None; ev.xclient.data.l[2] = stack_mode; ev.xclient.data.l[3] = 0; ev.xclient.data.l[4] = 0; 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); #if GTK_CHECK_VERSION (2, 10, 0) if (!gtk_check_version (2, 10, 0)) gtk_window_set_type_hint (GTK_WINDOW (tip_window), GDK_WINDOW_TYPE_HINT_TOOLTIP); #endif 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: 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: if (xevent->xbutton.button == 1) d->button_states[0] |= PRESSED_EVENT_WINDOW; break; case ButtonRelease: if (xevent->xbutton.button == 1) { 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: 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: if (xevent->xbutton.button == 1) d->button_states[1] |= PRESSED_EVENT_WINDOW; break; case ButtonRelease: if (xevent->xbutton.button == 1) { 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: 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: if (xevent->xbutton.button == 1) d->button_states[2] |= PRESSED_EVENT_WINDOW; break; case ButtonRelease: if (xevent->xbutton.button == 1) { 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: 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 position_action_menu (GtkMenu *menu, gint *x, gint *y, gboolean *push_in, gpointer user_data) { WnckWindow *win = (WnckWindow *) user_data; wnck_window_get_geometry (win, x, y, NULL, NULL); *push_in = TRUE; } static void action_menu_map (WnckWindow *win, long button, Time time) { GdkDisplay *gdkdisplay; GdkScreen *screen; gdkdisplay = gdk_display_get_default (); screen = gdk_display_get_default_screen (gdkdisplay); if (action_menu) { if (action_menu_mapped) { gtk_widget_destroy (action_menu); action_menu_mapped = FALSE; action_menu = NULL; return; } else gtk_widget_destroy (action_menu); } switch (wnck_window_get_window_type (win)) { case WNCK_WINDOW_DESKTOP: case WNCK_WINDOW_DOCK: /* don't allow window action */ return; case WNCK_WINDOW_NORMAL: case WNCK_WINDOW_DIALOG: case WNCK_WINDOW_TOOLBAR: case WNCK_WINDOW_MENU: case WNCK_WINDOW_UTILITY: case WNCK_WINDOW_SPLASHSCREEN: /* allow window action menu */ break; } 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); if (button) gtk_menu_popup (GTK_MENU (action_menu), NULL, NULL, NULL, NULL, button, time); else gtk_menu_popup (GTK_MENU (action_menu), NULL, NULL, position_action_menu, (gpointer) win, button, time); action_menu_mapped = TRUE; } static double square (double x) { return x * x; } static double dist (double x1, double y1, double x2, double y2) { return sqrt (square (x1 - x2) + square (y1 - y2)); } 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; static int last_button_x = 0; static int last_button_y = 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 && dist (xevent->xbutton.x, xevent->xbutton.y, last_button_x, last_button_y) < DOUBLE_CLICK_DISTANCE) { switch (double_click_action) { case DOUBLE_CLICK_SHADE: if (wnck_window_is_shaded (win)) wnck_window_unshade (win); else wnck_window_shade (win); break; case DOUBLE_CLICK_MAXIMIZE: if (wnck_window_is_maximized (win)) wnck_window_unmaximize (win); else wnck_window_maximize (win); default: break; } last_button_num = 0; last_button_xwindow = None; last_button_time = 0; last_button_x = 0; last_button_y = 0; } else { last_button_num = xevent->xbutton.button; last_button_xwindow = xevent->xbutton.window; last_button_time = xevent->xbutton.time; last_button_x = xevent->xbutton.x; last_button_y = xevent->xbutton.y; restack_window (win, Above); move_resize_window (win, WM_MOVERESIZE_MOVE, xevent); } } else if (xevent->xbutton.button == 2) { restack_window (win, Below); } else if (xevent->xbutton.button == 3) { action_menu_map (win, xevent->xbutton.button, xevent->xbutton.time); } else if (xevent->xbutton.button == 4) { if(!wnck_window_is_shaded(win)) wnck_window_shade(win); } else if (xevent->xbutton.button == 5) { if(wnck_window_is_shaded(win)) wnck_window_unshade(win); } } 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 void panel_action (Display *xdisplay, Window root, Atom panel_action, Time event_time) { XEvent ev; ev.type = ClientMessage; ev.xclient.window = root; ev.xclient.message_type = panel_action_atom; ev.xclient.format = 32; ev.xclient.data.l[0] = panel_action; ev.xclient.data.l[1] = event_time; ev.xclient.data.l[2] = 0; ev.xclient.data.l[3] = 0; ev.xclient.data.l[4] = 0; XSendEvent (xdisplay, root, FALSE, StructureNotifyMask, &ev); } static void force_quit_dialog_realize (GtkWidget *dialog, void *data) { WnckWindow *win = data; gdk_error_trap_push (); XSetTransientForHint (gdk_display, GDK_WINDOW_XID (dialog->window), wnck_window_get_xid (win)); XSync (gdk_display, FALSE); gdk_error_trap_pop (); } static char * get_client_machine (Window xwindow) { Atom atom, type; gulong nitems, bytes_after; gchar *str = NULL; int format, result; char *retval; atom = XInternAtom (gdk_display, "WM_CLIENT_MACHINE", FALSE); gdk_error_trap_push (); result = XGetWindowProperty (gdk_display, xwindow, atom, 0, G_MAXLONG, FALSE, XA_STRING, &type, &format, &nitems, &bytes_after, (guchar **) &str); gdk_error_trap_pop (); if (result != Success) return NULL; if (type != XA_STRING) { XFree (str); return NULL; } retval = g_strdup (str); XFree (str); return retval; } static void kill_window (WnckWindow *win) { WnckApplication *app; app = wnck_window_get_application (win); if (app) { gchar buf[257], *client_machine; int pid; pid = wnck_application_get_pid (app); client_machine = get_client_machine (wnck_application_get_xid (app)); if (client_machine && pid > 0) { if (gethostname (buf, sizeof (buf) - 1) == 0) { if (strcmp (buf, client_machine) == 0) kill (pid, 9); } } if (client_machine) g_free (client_machine); } gdk_error_trap_push (); XKillClient (gdk_display, wnck_window_get_xid (win)); XSync (gdk_display, FALSE); gdk_error_trap_pop (); } static void force_quit_dialog_response (GtkWidget *dialog, gint response, void *data) { WnckWindow *win = data; decor_t *d = g_object_get_data (G_OBJECT (win), "decor"); if (response == GTK_RESPONSE_ACCEPT) kill_window (win); if (d->force_quit_dialog) { d->force_quit_dialog = NULL; gtk_widget_destroy (dialog); } } static void show_force_quit_dialog (WnckWindow *win, Time timestamp) { decor_t *d = g_object_get_data (G_OBJECT (win), "decor"); GtkWidget *dialog; gchar *str, *tmp; if (d->force_quit_dialog) return; tmp = g_markup_escape_text (wnck_window_get_name (win), -1); str = g_strdup_printf ("The window \"%s\" is not responding.", tmp); g_free (tmp); dialog = gtk_message_dialog_new (NULL, 0, GTK_MESSAGE_WARNING, GTK_BUTTONS_NONE, "%s\n\n%s", str, "Forcing this application to " "quit will cause you to lose any " "unsaved changes."); g_free (str); gtk_window_set_icon_name (GTK_WINDOW (dialog), "force-quit"); gtk_label_set_use_markup (GTK_LABEL (GTK_MESSAGE_DIALOG (dialog)->label), TRUE); gtk_label_set_line_wrap (GTK_LABEL (GTK_MESSAGE_DIALOG (dialog)->label), TRUE); gtk_dialog_add_buttons (GTK_DIALOG (dialog), GTK_STOCK_CANCEL, GTK_RESPONSE_REJECT, "_Force Quit", GTK_RESPONSE_ACCEPT, NULL); gtk_dialog_set_default_response (GTK_DIALOG (dialog), GTK_RESPONSE_REJECT); g_signal_connect (G_OBJECT (dialog), "realize", G_CALLBACK (force_quit_dialog_realize), win); g_signal_connect (G_OBJECT (dialog), "response", G_CALLBACK (force_quit_dialog_response), win); gtk_window_set_modal (GTK_WINDOW (dialog), TRUE); gtk_widget_realize (dialog); gdk_x11_window_set_user_time (dialog->window, timestamp); gtk_widget_show (dialog); d->force_quit_dialog = dialog; } static void hide_force_quit_dialog (WnckWindow *win) { decor_t *d = g_object_get_data (G_OBJECT (win), "decor"); if (d->force_quit_dialog) { gtk_widget_destroy (d->force_quit_dialog); d->force_quit_dialog = NULL; } } 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, window; if (!get_window_prop (xid, select_window_atom, &window)) { if (get_window_prop (xid, frame_window_atom, &frame)) add_frame_window (win, frame); else remove_frame_window (win); } } } else if (xevent->xproperty.atom == mwm_hints_atom) { WnckWindow *win; xid = xevent->xproperty.window; win = wnck_window_get (xid); if (win) { decor_t *d = g_object_get_data (G_OBJECT (win), "decor"); gboolean decorated = FALSE; if (get_mwm_prop (xid) & (MWM_DECOR_ALL | MWM_DECOR_TITLE)) decorated = TRUE; if (decorated != d->decorated) { d->decorated = decorated; if (decorated) { d->context = NULL; d->width = d->height = 0; update_window_decoration_size (win); update_event_windows (win); } else { gdk_error_trap_push (); XDeleteProperty (xdisplay, xid, win_decor_atom); XSync (xdisplay, FALSE); gdk_error_trap_pop (); } } } } else if (xevent->xproperty.atom == select_window_atom) { WnckWindow *win; xid = xevent->xproperty.window; win = wnck_window_get (xid); if (win) { Window select; if (get_window_prop (xid, select_window_atom, &select)) update_switcher_window (win, select); } } break; case DestroyNotify: g_hash_table_remove (frame_table, GINT_TO_POINTER (xevent->xproperty.window)); break; case ClientMessage: if (xevent->xclient.message_type == toolkit_action_atom) { long action; action = xevent->xclient.data.l[0]; if (action == toolkit_action_main_menu_atom) { panel_action (xdisplay, xevent->xclient.window, panel_action_main_menu_atom, xevent->xclient.data.l[1]); } else if (action == toolkit_action_run_dialog_atom) { panel_action (xdisplay, xevent->xclient.window, panel_action_run_dialog_atom, xevent->xclient.data.l[1]); } else if (action == toolkit_action_window_menu_atom) { WnckWindow *win; win = wnck_window_get (xevent->xclient.window); if (win) { action_menu_map (win, xevent->xclient.data.l[2], xevent->xclient.data.l[1]); } } else if (action == toolkit_action_force_quit_dialog_atom) { WnckWindow *win; win = wnck_window_get (xevent->xclient.window); if (win) { if (xevent->xclient.data.l[2]) show_force_quit_dialog (win, xevent->xclient.data.l[1]); else hide_force_quit_dialog (win); } } } 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; } static GdkFilterReturn selection_event_filter_func (GdkXEvent *gdkxevent, GdkEvent *event, gpointer data) { Display *xdisplay; GdkDisplay *gdkdisplay; XEvent *xevent = gdkxevent; int status; gdkdisplay = gdk_display_get_default (); xdisplay = GDK_DISPLAY_XDISPLAY (gdkdisplay); switch (xevent->type) { case SelectionRequest: decor_handle_selection_request (xdisplay, xevent, dm_sn_timestamp); break; case SelectionClear: status = decor_handle_selection_clear (xdisplay, xevent, 0); if (status == DECOR_SELECTION_GIVE_UP) exit (0); default: break; } 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 update_style (GtkWidget *widget) { GtkStyle *style; decor_color_t spot_color; style = gtk_widget_get_style (widget); gtk_style_attach (style, widget->window); 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); } /* to save some memory, value is specific to current decorations */ #define TRANSLUCENT_CORNER_SIZE 3 static void draw_border_shape (Display *xdisplay, Pixmap pixmap, Picture picture, int width, int height, decor_context_t *c, void *closure) { static XRenderColor white = { 0xffff, 0xffff, 0xffff, 0xffff }; GdkScreen *screen; GdkColormap *colormap; decor_t d; double save_decoration_alpha; memset (&d, 0, sizeof (d)); d.pixmap = gdk_pixmap_foreign_new_for_display (gdk_display_get_default (), pixmap); d.width = width; d.height = height; d.active = TRUE; d.draw = theme_draw_window_decoration; d.picture = picture; d.context = c; /* we use closure argument if maximized */ if (closure) d.state |= WNCK_WINDOW_STATE_MAXIMIZED_HORIZONTALLY | WNCK_WINDOW_STATE_MAXIMIZED_VERTICALLY; decor_get_default_layout (c, 1, 1, &d.border_layout); screen = gdk_display_get_default_screen (gdk_display_get_default ()); colormap = gdk_screen_get_rgba_colormap (screen); gdk_drawable_set_colormap (d.pixmap, colormap); /* create shadow from opaque decoration */ save_decoration_alpha = decoration_alpha; decoration_alpha = 1.0; (*d.draw) (&d); decoration_alpha = save_decoration_alpha; XRenderFillRectangle (xdisplay, PictOpSrc, picture, &white, c->left_space, c->top_space, width - c->left_space - c->right_space, height - c->top_space - c->bottom_space); gdk_pixmap_unref (d.pixmap); } static int update_shadow (void) { decor_shadow_options_t opt; Display *xdisplay = gdk_display; GdkDisplay *display = gdk_display_get_default (); GdkScreen *screen = gdk_display_get_default_screen (display); opt.shadow_radius = shadow_radius; opt.shadow_opacity = shadow_opacity; memcpy (opt.shadow_color, shadow_color, sizeof (shadow_color)); opt.shadow_offset_x = shadow_offset_x; opt.shadow_offset_y = shadow_offset_y; if (no_border_shadow) { decor_shadow_destroy (xdisplay, no_border_shadow); no_border_shadow = NULL; } no_border_shadow = decor_shadow_create (xdisplay, gdk_x11_screen_get_xscreen (screen), 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, &opt, &shadow_context, decor_draw_simple, 0); if (border_shadow) { decor_shadow_destroy (xdisplay, border_shadow); border_shadow = NULL; } border_shadow = decor_shadow_create (xdisplay, gdk_x11_screen_get_xscreen (screen), 1, 1, _win_extents.left, _win_extents.right, _win_extents.top + titlebar_height, _win_extents.bottom, _win_extents.left - _win_extents.left - TRANSLUCENT_CORNER_SIZE, _win_extents.right - TRANSLUCENT_CORNER_SIZE, _win_extents.top + titlebar_height - TRANSLUCENT_CORNER_SIZE, _win_extents.bottom - TRANSLUCENT_CORNER_SIZE, &opt, &window_context, draw_border_shape, 0); if (max_border_shadow) { decor_shadow_destroy (xdisplay, max_border_shadow); max_border_shadow = NULL; } max_border_shadow = decor_shadow_create (xdisplay, gdk_x11_screen_get_xscreen (screen), 1, 1, _max_win_extents.left, _max_win_extents.right, _max_win_extents.top + max_titlebar_height, _max_win_extents.bottom, _max_win_extents.left - _max_win_extents.left - TRANSLUCENT_CORNER_SIZE, _max_win_extents.right - TRANSLUCENT_CORNER_SIZE, _max_win_extents.top + max_titlebar_height - TRANSLUCENT_CORNER_SIZE, _max_win_extents.bottom - TRANSLUCENT_CORNER_SIZE, &opt, &max_window_context, draw_border_shape, (void *) 1); if (switcher_shadow) { decor_shadow_destroy (xdisplay, switcher_shadow); switcher_shadow = NULL; } switcher_shadow = decor_shadow_create (xdisplay, gdk_x11_screen_get_xscreen (screen), 1, 1, _switcher_extents.left, _switcher_extents.right, _switcher_extents.top, _switcher_extents.bottom, _switcher_extents.left - TRANSLUCENT_CORNER_SIZE, _switcher_extents.right - TRANSLUCENT_CORNER_SIZE, _switcher_extents.top - TRANSLUCENT_CORNER_SIZE, _switcher_extents.bottom - TRANSLUCENT_CORNER_SIZE, &opt, &switcher_context, decor_draw_simple, 0); return 1; } static void update_window_decoration (WnckWindow *win) { decor_t *d = g_object_get_data (G_OBJECT (win), "decor"); if (d->decorated) { /* force size update */ d->context = NULL; d->width = d->height = 0; update_window_decoration_size (win); update_event_windows (win); } else { Window xid = wnck_window_get_xid (win); Window select; if (get_window_prop (xid, select_window_atom, &select)) { /* force size update */ d->context = NULL; d->width = d->height = 0; switcher_width = switcher_height = 0; update_switcher_window (win, select); } } } static void style_changed (GtkWidget *widget) { GdkDisplay *gdkdisplay; GdkScreen *gdkscreen; WnckScreen *screen; GList *windows; gdkdisplay = gdk_display_get_default (); gdkscreen = gdk_display_get_default_screen (gdkdisplay); screen = wnck_screen_get_default (); update_style (widget); update_default_decorations (gdkscreen); if (minimal) return; windows = wnck_screen_get_windows (screen); while (windows != NULL) { update_window_decoration (WNCK_WINDOW (windows->data)); windows = windows->next; } } static const PangoFontDescription * get_titlebar_font (void) { if (use_system_font) { return NULL; } else return titlebar_font; } static void titlebar_font_changed (GConfClient *client) { gchar *str; str = gconf_client_get_string (client, COMPIZ_TITLEBAR_FONT_KEY, NULL); if (!str) str = g_strdup ("Sans Bold 12"); if (titlebar_font) pango_font_description_free (titlebar_font); titlebar_font = pango_font_description_from_string (str); g_free (str); } static void double_click_titlebar_changed (GConfClient *client) { gchar *action; double_click_action = DOUBLE_CLICK_MAXIMIZE; action = gconf_client_get_string (client, COMPIZ_DOUBLE_CLICK_TITLEBAR_KEY, NULL); if (action) { if (strcmp (action, "toggle_shade") == 0) double_click_action = DOUBLE_CLICK_SHADE; else if (strcmp (action, "toggle_maximize") == 0) double_click_action = DOUBLE_CLICK_MAXIMIZE; g_free (action); } } static void meta_update_border_extents (gint text_height) { MetaTheme *theme; gint top_height, bottom_height, left_width, right_width; theme = meta_theme_get_current (); meta_theme_get_frame_borders (theme, META_FRAME_TYPE_NORMAL, text_height, 0, &top_height, &bottom_height, &left_width, &right_width); _win_extents.top = _default_win_extents.top; _win_extents.bottom = bottom_height; _win_extents.left = left_width; _win_extents.right = right_width; titlebar_height = top_height - _win_extents.top; meta_theme_get_frame_borders (theme, META_FRAME_TYPE_NORMAL, text_height, META_FRAME_MAXIMIZED, &top_height, &bottom_height, &left_width, &right_width); _max_win_extents.top = _default_win_extents.top; _max_win_extents.bottom = bottom_height; _max_win_extents.left = left_width; _max_win_extents.right = right_width; max_titlebar_height = top_height - _max_win_extents.top; } static void update_titlebar_font (void) { const PangoFontDescription *font_desc; PangoFontMetrics *metrics; PangoLanguage *lang; font_desc = get_titlebar_font (); if (!font_desc) { GtkStyle *default_style; default_style = gtk_widget_get_default_style (); font_desc = default_style->font_desc; } pango_context_set_font_description (pango_context, font_desc); lang = pango_context_get_language (pango_context); metrics = pango_context_get_metrics (pango_context, font_desc, lang); text_height = PANGO_PIXELS (pango_font_metrics_get_ascent (metrics) + pango_font_metrics_get_descent (metrics)); pango_font_metrics_unref (metrics); } static gboolean shadow_settings_changed (GConfClient *client) { return FALSE; double radius, opacity; int offset; gchar *color; gboolean changed = FALSE; radius = gconf_client_get_float (client, COMPIZ_SHADOW_RADIUS_KEY, NULL); radius = MAX (0.0, MIN (radius, 48.0)); if (shadow_radius != radius) { shadow_radius = radius; changed = TRUE; } opacity = gconf_client_get_float (client, COMPIZ_SHADOW_OPACITY_KEY, NULL); opacity = MAX (0.0, MIN (opacity, 6.0)); if (shadow_opacity != opacity) { shadow_opacity = opacity; changed = TRUE; } color = gconf_client_get_string (client, COMPIZ_SHADOW_COLOR_KEY, NULL); if (color) { int c[4]; if (sscanf (color, "#%2x%2x%2x%2x", &c[0], &c[1], &c[2], &c[3]) == 4) { shadow_color[0] = c[0] << 8 | c[0]; shadow_color[1] = c[1] << 8 | c[1]; shadow_color[2] = c[2] << 8 | c[2]; changed = TRUE; } g_free (color); } offset = gconf_client_get_int (client, COMPIZ_SHADOW_OFFSET_X_KEY, NULL); offset = MAX (-16, MIN (offset, 16)); if (shadow_offset_x != offset) { shadow_offset_x = offset; changed = TRUE; } offset = gconf_client_get_int (client, COMPIZ_SHADOW_OFFSET_Y_KEY, NULL); offset = MAX (-16, MIN (offset, 16)); if (shadow_offset_y != offset) { shadow_offset_y = offset; changed = TRUE; } return changed; } static void bell_settings_changed (GConfClient *client) { gboolean audible, visual, fullscreen; gchar *type; audible = gconf_client_get_bool (client, META_AUDIBLE_BELL_KEY, NULL); visual = gconf_client_get_bool (client, META_VISUAL_BELL_KEY, NULL); type = gconf_client_get_string (client, META_VISUAL_BELL_TYPE_KEY, NULL); if (type && strcmp (type, "fullscreen") == 0) fullscreen = TRUE; else fullscreen = FALSE; g_free (type); gconf_client_set_bool (client, COMPIZ_AUDIBLE_BELL_KEY, audible, NULL); gconf_client_set_bool (client, COMPIZ_VISUAL_BELL_KEY, visual, NULL); gconf_client_set_bool (client, COMPIZ_FULLSCREEN_VISUAL_BELL_KEY, fullscreen, NULL); } static gboolean theme_changed (GConfClient *client) { gchar *theme; theme = gconf_client_get_string (client, META_THEME_KEY, NULL); if (theme) { meta_theme_set_current (theme, TRUE); g_free (theme); } theme_draw_window_decoration = meta_draw_window_decoration; theme_calc_decoration_size = meta_calc_decoration_size; theme_update_border_extents = meta_update_border_extents; theme_get_event_window_position = meta_get_event_window_position; theme_get_button_position = meta_get_button_position; return TRUE; } static gboolean theme_opacity_changed (GConfClient *client) { return FALSE; gboolean shade_opacity, changed = FALSE; gdouble opacity; opacity = gconf_client_get_float (client, META_THEME_OPACITY_KEY, NULL); if (opacity != meta_opacity) { meta_opacity = opacity; changed = TRUE; } if (opacity < 1.0) { shade_opacity = gconf_client_get_bool (client, META_THEME_SHADE_OPACITY_KEY, NULL); if (shade_opacity != meta_shade_opacity) { meta_shade_opacity = shade_opacity; changed = TRUE; } } opacity = gconf_client_get_float (client, META_THEME_ACTIVE_OPACITY_KEY, NULL); if (opacity != meta_active_opacity) { meta_active_opacity = opacity; changed = TRUE; } if (opacity < 1.0) { shade_opacity = gconf_client_get_bool (client, META_THEME_ACTIVE_SHADE_OPACITY_KEY, NULL); if (shade_opacity != meta_active_shade_opacity) { meta_active_shade_opacity = shade_opacity; changed = TRUE; } } return changed; } static void value_changed (GConfClient *client, const gchar *key, GConfValue *value, void *data) { gboolean changed = FALSE; if (strcmp (key, COMPIZ_USE_SYSTEM_FONT_KEY) == 0) { if (gconf_client_get_bool (client, COMPIZ_USE_SYSTEM_FONT_KEY, NULL) != use_system_font) { use_system_font = !use_system_font; changed = TRUE; } } else if (strcmp (key, COMPIZ_TITLEBAR_FONT_KEY) == 0) { titlebar_font_changed (client); changed = !use_system_font; } else if (strcmp (key, COMPIZ_DOUBLE_CLICK_TITLEBAR_KEY) == 0) { double_click_titlebar_changed (client); } else if (strcmp (key, COMPIZ_SHADOW_RADIUS_KEY) == 0 || strcmp (key, COMPIZ_SHADOW_OPACITY_KEY) == 0 || strcmp (key, COMPIZ_SHADOW_OFFSET_X_KEY) == 0 || strcmp (key, COMPIZ_SHADOW_OFFSET_Y_KEY) == 0 || strcmp (key, COMPIZ_SHADOW_COLOR_KEY) == 0) { if (shadow_settings_changed (client)) changed = TRUE; } else if (strcmp (key, META_AUDIBLE_BELL_KEY) == 0 || strcmp (key, META_VISUAL_BELL_KEY) == 0 || strcmp (key, META_VISUAL_BELL_TYPE_KEY) == 0) { bell_settings_changed (client); } else if ( strcmp (key, META_THEME_KEY) == 0) { if (theme_changed (client)) changed = TRUE; } else if (strcmp (key, META_THEME_OPACITY_KEY) == 0 || strcmp (key, META_THEME_SHADE_OPACITY_KEY) == 0 || strcmp (key, META_THEME_ACTIVE_OPACITY_KEY) == 0 || strcmp (key, META_THEME_ACTIVE_SHADE_OPACITY_KEY) == 0) { if (theme_opacity_changed (client)) changed = TRUE; } if (changed) { GdkDisplay *gdkdisplay; GdkScreen *gdkscreen; WnckScreen *screen = data; GList *windows; gdkdisplay = gdk_display_get_default (); gdkscreen = gdk_display_get_default_screen (gdkdisplay); update_titlebar_font (); (*theme_update_border_extents) (text_height); update_shadow (); update_default_decorations (gdkscreen); if (minimal) return; windows = wnck_screen_get_windows (screen); while (windows != NULL) { decor_t *d = g_object_get_data (G_OBJECT (windows->data), "decor"); if (d->decorated) { if ( d->draw == meta_draw_window_decoration) d->draw = theme_draw_window_decoration; } update_window_decoration (WNCK_WINDOW (windows->data)); windows = windows->next; } } } static gboolean init_settings (WnckScreen *screen) { GtkSettings *settings; GConfClient *gconf; GdkScreen *gdkscreen; GdkColormap *colormap; gconf = gconf_client_get_default (); gconf_client_add_dir (gconf, GCONF_DIR, GCONF_CLIENT_PRELOAD_ONELEVEL, NULL); gconf_client_add_dir (gconf, METACITY_GCONF_DIR, GCONF_CLIENT_PRELOAD_ONELEVEL, NULL); gconf_client_add_dir (gconf, COMPIZ_GCONF_DIR1, GCONF_CLIENT_PRELOAD_ONELEVEL, NULL); gconf_client_add_dir (gconf, COMPIZ_GCONF_DIR2, GCONF_CLIENT_PRELOAD_ONELEVEL, NULL); gconf_client_add_dir (gconf, COMPIZ_GCONF_DIR3, GCONF_CLIENT_PRELOAD_ONELEVEL, NULL); g_signal_connect (G_OBJECT (gconf), "value_changed", G_CALLBACK (value_changed), screen); style_window = gtk_window_new (GTK_WINDOW_POPUP); gdkscreen = gdk_display_get_default_screen (gdk_display_get_default ()); colormap = gdk_screen_get_rgba_colormap (gdkscreen); if (colormap) gtk_widget_set_colormap (style_window, colormap); gtk_widget_realize (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); use_system_font = gconf_client_get_bool (gconf, COMPIZ_USE_SYSTEM_FONT_KEY, NULL); theme_changed (gconf); theme_opacity_changed (gconf); update_style (style_window); titlebar_font_changed (gconf); update_titlebar_font (); double_click_titlebar_changed (gconf); shadow_settings_changed (gconf); bell_settings_changed (gconf); (*theme_update_border_extents) (text_height); update_shadow (); return TRUE; } int main (int argc, char *argv[]) { GdkDisplay *gdkdisplay; Display *xdisplay; GdkScreen *gdkscreen; WnckScreen *screen; gint i, j, status; gboolean replace = FALSE; program_name = argv[0]; gtk_init (&argc, &argv); for (i = 0; i < argc; i++) { if (strcmp (argv[i], "--minimal") == 0) { minimal = TRUE; } else if (strcmp (argv[i], "--replace") == 0) { replace = TRUE; } else if (strcmp (argv[i], "--help") == 0) { fprintf (stderr, "%s [--minimal] [--replace] [--help]\n", program_name); return 0; } } 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); wm_move_resize_atom = XInternAtom (xdisplay, "_NET_WM_MOVERESIZE", FALSE); restack_window_atom = XInternAtom (xdisplay, "_NET_RESTACK_WINDOW", FALSE); select_window_atom = XInternAtom (xdisplay, "_SWITCH_SELECT_WINDOW", FALSE); mwm_hints_atom = XInternAtom (xdisplay, "_MOTIF_WM_HINTS", FALSE); toolkit_action_atom = XInternAtom (xdisplay, "_COMPIZ_TOOLKIT_ACTION", FALSE); toolkit_action_main_menu_atom = XInternAtom (xdisplay, "_COMPIZ_TOOLKIT_ACTION_MAIN_MENU", FALSE); toolkit_action_run_dialog_atom = XInternAtom (xdisplay, "_COMPIZ_TOOLKIT_ACTION_RUN_DIALOG", FALSE); toolkit_action_window_menu_atom = XInternAtom (xdisplay, "_COMPIZ_TOOLKIT_ACTION_WINDOW_MENU", FALSE); toolkit_action_force_quit_dialog_atom = XInternAtom (xdisplay, "_COMPIZ_TOOLKIT_ACTION_FORCE_QUIT_DIALOG", FALSE); panel_action_atom = XInternAtom (xdisplay, "_GNOME_PANEL_ACTION", FALSE); panel_action_main_menu_atom = XInternAtom (xdisplay, "_GNOME_PANEL_ACTION_MAIN_MENU", FALSE); panel_action_run_dialog_atom = XInternAtom (xdisplay, "_GNOME_PANEL_ACTION_RUN_DIALOG", FALSE); status = decor_acquire_dm_session (xdisplay, DefaultScreen(xdisplay), "gwd", replace, &dm_sn_timestamp); if (status != DECOR_ACQUIRE_STATUS_SUCCESS) { if (status == DECOR_ACQUIRE_STATUS_OTHER_DM_RUNNING) { fprintf (stderr, "%s: Could not acquire decoration manager " "selection on screen %d display \"%s\"\n", program_name, DefaultScreen(xdisplay), DisplayString (xdisplay)); } else if (status == DECOR_ACQUIRE_STATUS_OTHER_DM_RUNNING) { fprintf (stderr, "%s: Screen %d on display \"%s\" already " "has a decoration manager; try using the " "--replace option to replace the current " "decoration manager.\n", program_name, DefaultScreen(xdisplay), DisplayString (xdisplay)); } return 1; } 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); } } xformat = XRenderFindStandardFormat (xdisplay, PictStandardARGB32); frame_table = g_hash_table_new (NULL, NULL); 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, selection_event_filter_func, NULL); if (!minimal) { 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; } decor_set_dm_check_hint (xdisplay, DefaultScreen(xdisplay)); update_default_decorations (gdkscreen); gtk_main (); return 0; }