mirror of
https://github.com/brl/mutter.git
synced 2025-02-17 21:54:10 +00:00
![Elijah Newren](/assets/img/avatar_default.png)
2006-10-01 Elijah Newren <newren gmail com> * src/*.[ch]: Stick an emacs comment directive at the beginning of all the code files so that people using emacs will be more likely to get coding style correct in their patches. We still need a similar vi directive. #358866
1263 lines
28 KiB
C
1263 lines
28 KiB
C
/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
|
|
|
|
/*
|
|
* Copyright (C) 2006 Red Hat, Inc.
|
|
*
|
|
* This program is free software; you can redistribute it and/or
|
|
* modify it under the terms of the GNU General Public License as
|
|
* published by the Free Software Foundation; either version 2 of the
|
|
* License, or (at your option) any later version.
|
|
*
|
|
* This program is distributed in the hope that it will be useful, but
|
|
* WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
* General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU General Public License
|
|
* along with this program; if not, write to the Free Software
|
|
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
|
|
* 02111-1307, USA.
|
|
*/
|
|
|
|
#include <config.h>
|
|
|
|
#ifdef HAVE_COMPOSITE_EXTENSIONS
|
|
|
|
#include <X11/Xlib.h>
|
|
#include <glib.h>
|
|
#include <cm/ws.h>
|
|
#include <cm/wsint.h>
|
|
#include <cm/node.h>
|
|
#include <cm/drawable-node.h>
|
|
#include <string.h>
|
|
#include <math.h>
|
|
|
|
#include "effects.h"
|
|
#include "c-window.h"
|
|
#include "window.h"
|
|
#include "frame.h"
|
|
#include "spring-model.h"
|
|
|
|
typedef struct UnminimizeInfo UnminimizeInfo;
|
|
|
|
struct _MetaCompWindow
|
|
{
|
|
MetaDisplay *display;
|
|
MetaScreen *screen;
|
|
WsDrawable *drawable;
|
|
WsPixmap *pixmap;
|
|
CmDrawableNode *node;
|
|
gboolean updates;
|
|
WsSyncAlarm *alarm;
|
|
|
|
WsRectangle size;
|
|
gboolean waiting_for_paint;
|
|
|
|
gint64 counter_value;
|
|
gint ref_count;
|
|
|
|
gboolean animation_in_progress;
|
|
gboolean hide_after_animation;
|
|
|
|
int stack_freeze_count;
|
|
int fade_in_idle_id;
|
|
|
|
MetaCompWindowDestroy destroy;
|
|
gpointer closure;
|
|
|
|
UnminimizeInfo *unminimize_info;
|
|
};
|
|
|
|
static void cancel_fade (MetaCompWindow *comp_window);
|
|
static void start_unminimize (MetaCompWindow *comp_window);
|
|
|
|
static Window
|
|
find_app_window (MetaCompWindow *comp_window)
|
|
{
|
|
Window xwindow = WS_RESOURCE_XID (comp_window->drawable);
|
|
MetaWindow *meta_window =
|
|
meta_display_lookup_x_window (comp_window->display, xwindow);
|
|
|
|
if (meta_window)
|
|
return meta_window->xwindow;
|
|
else
|
|
return xwindow;
|
|
}
|
|
|
|
static WsPixmap *
|
|
take_snapshot (WsDrawable *drawable)
|
|
{
|
|
WsDisplay *display = WS_RESOURCE (drawable)->display;
|
|
WsRectangle geometry;
|
|
WsPixmap *pixmap;
|
|
|
|
ws_display_begin_error_trap (display);
|
|
|
|
ws_drawable_query_geometry (drawable, &geometry);
|
|
|
|
pixmap = ws_pixmap_new (drawable, geometry.width, geometry.height);
|
|
|
|
ws_drawable_copy_area (drawable, 0, 0, geometry.width, geometry.height,
|
|
WS_DRAWABLE (pixmap), 0, 0,
|
|
NULL);
|
|
|
|
ws_display_end_error_trap (display);
|
|
|
|
return pixmap;
|
|
}
|
|
|
|
static void
|
|
on_alarm (WsSyncAlarm *alarm,
|
|
WsAlarmNotifyEvent *event,
|
|
MetaCompWindow *window)
|
|
{
|
|
if (window->pixmap)
|
|
g_object_unref (window->pixmap);
|
|
|
|
window->pixmap = take_snapshot (window->drawable);
|
|
|
|
ws_sync_alarm_set (window->alarm, event->counter_value + 2);
|
|
ws_sync_counter_change (event->counter, 1);
|
|
}
|
|
|
|
static gboolean
|
|
has_counter (MetaCompWindow *comp_window)
|
|
{
|
|
Window xwindow = find_app_window (comp_window);
|
|
WsDisplay *display = WS_RESOURCE (comp_window->drawable)->display;
|
|
WsWindow *window = ws_window_lookup (display, xwindow);
|
|
WsSyncCounter *counter;
|
|
|
|
ws_display_init_sync (display);
|
|
|
|
counter = ws_window_get_property_sync_counter (
|
|
window, "_NET_WM_FINISH_FRAME_COUNTER");
|
|
|
|
if (counter)
|
|
{
|
|
WsSyncAlarm *alarm;
|
|
gint64 value = ws_sync_counter_query_value (counter);
|
|
|
|
#if 0
|
|
g_print ("counter value %lld\n", ws_sync_counter_query_value (counter));
|
|
alarm = ws_sync_alarm_new (WS_RESOURCE (comp_window->drawable)->display,
|
|
|
|
display, counter);
|
|
#endif
|
|
|
|
g_signal_connect (alarm, "alarm_notify_event",
|
|
G_CALLBACK (on_alarm), comp_window);
|
|
|
|
if (value % 2 == 1)
|
|
{
|
|
ws_sync_alarm_set (alarm, value + 2);
|
|
|
|
#if 0
|
|
g_print ("wait for %lld\n", value + 2);
|
|
|
|
g_print ("increasing counter\n");
|
|
#endif
|
|
ws_sync_counter_change (counter, 1);
|
|
|
|
#if 0
|
|
g_print ("counter value %lld\n",
|
|
ws_sync_counter_query_value (counter));
|
|
#endif
|
|
}
|
|
else
|
|
{
|
|
#if 0
|
|
g_print ("wait for %lld\n", value + 1);
|
|
#endif
|
|
ws_sync_alarm_set (alarm, value + 1);
|
|
}
|
|
|
|
comp_window->alarm = alarm;
|
|
|
|
}
|
|
|
|
if (counter)
|
|
return TRUE;
|
|
else
|
|
return FALSE;
|
|
|
|
#if 0
|
|
if (counter)
|
|
{
|
|
g_print ("found counter %lx on %lx\n",
|
|
WS_RESOURCE_XID (counter),
|
|
WS_RESOURCE_XID (window));
|
|
}
|
|
else
|
|
{
|
|
g_print ("no counter found for %lx\n", WS_RESOURCE_XID (window));
|
|
}
|
|
#endif
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
void
|
|
meta_comp_window_show (MetaCompWindow *comp_window)
|
|
{
|
|
if (comp_window->animation_in_progress)
|
|
comp_window->hide_after_animation = FALSE;
|
|
|
|
cm_drawable_node_set_viewable (CM_DRAWABLE_NODE (comp_window->node), TRUE);
|
|
cm_drawable_node_update_pixmap (CM_DRAWABLE_NODE (comp_window->node));
|
|
}
|
|
|
|
void
|
|
meta_comp_window_hide (MetaCompWindow *comp_window)
|
|
{
|
|
if (comp_window->animation_in_progress)
|
|
{
|
|
comp_window->hide_after_animation = TRUE;
|
|
return;
|
|
}
|
|
|
|
cancel_fade (comp_window);
|
|
|
|
cm_drawable_node_set_viewable (CM_DRAWABLE_NODE (comp_window->node),
|
|
FALSE);
|
|
}
|
|
|
|
MetaCompWindow *
|
|
meta_comp_window_new (MetaScreen *screen,
|
|
WsDrawable *drawable,
|
|
MetaCompWindowDestroy destroy,
|
|
gpointer closure)
|
|
{
|
|
MetaDisplay *display = screen->display;
|
|
MetaCompWindow *window;
|
|
WsRectangle geometry;
|
|
|
|
ws_drawable_query_geometry (drawable, &geometry);
|
|
|
|
window = g_new0 (MetaCompWindow, 1);
|
|
|
|
window->screen = screen;
|
|
window->display = display;
|
|
window->drawable = g_object_ref (drawable);
|
|
window->node = cm_drawable_node_new (drawable, &geometry);
|
|
window->updates = TRUE;
|
|
window->counter_value = 1;
|
|
window->ref_count = 1;
|
|
window->destroy = destroy;
|
|
window->closure = closure;
|
|
|
|
meta_comp_window_hide (window);
|
|
|
|
return window;
|
|
}
|
|
|
|
static MetaCompWindow *
|
|
comp_window_ref (MetaCompWindow *comp_window)
|
|
{
|
|
comp_window->ref_count++;
|
|
|
|
return comp_window;
|
|
}
|
|
|
|
static gboolean
|
|
comp_window_unref (MetaCompWindow *comp_window)
|
|
{
|
|
if (--comp_window->ref_count == 0)
|
|
{
|
|
if (comp_window->destroy)
|
|
comp_window->destroy (comp_window, comp_window->closure);
|
|
|
|
g_object_unref (comp_window->drawable);
|
|
g_object_unref (comp_window->node);
|
|
if (comp_window->alarm)
|
|
g_object_unref (comp_window->alarm);
|
|
memset (comp_window, 'e', sizeof (MetaCompWindow));
|
|
g_free (comp_window);
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
gboolean
|
|
meta_comp_window_free (MetaCompWindow *window)
|
|
{
|
|
return comp_window_unref (window);
|
|
}
|
|
|
|
void
|
|
meta_comp_window_set_size (MetaCompWindow *comp_window,
|
|
WsRectangle *rect)
|
|
{
|
|
if (comp_window->updates)
|
|
{
|
|
WsWindow *window = WS_WINDOW (comp_window->drawable);
|
|
WsDisplay *display = WS_RESOURCE (window)->display;
|
|
CmDrawableNode *dnode = CM_DRAWABLE_NODE (comp_window->node);
|
|
WsRegion *shape;
|
|
|
|
ws_display_begin_error_trap (display);
|
|
|
|
cm_drawable_node_set_geometry (dnode, rect);
|
|
shape = ws_window_get_output_shape (window);
|
|
cm_drawable_node_set_shape (dnode, shape);
|
|
ws_region_destroy (shape);
|
|
|
|
if (rect->width != comp_window->size.width ||
|
|
rect->height != comp_window->size.height)
|
|
{
|
|
cm_drawable_node_update_pixmap (dnode);
|
|
}
|
|
|
|
comp_window->size = *rect;
|
|
|
|
ws_display_end_error_trap (display);
|
|
}
|
|
}
|
|
|
|
static gboolean
|
|
has_type (WsWindow *window, const char *check_type)
|
|
{
|
|
gchar **types = ws_window_get_property_atom_list (window, "_NET_WM_WINDOW_TYPE");
|
|
int i;
|
|
gboolean result;
|
|
|
|
if (!types)
|
|
return FALSE;
|
|
|
|
result = FALSE;
|
|
|
|
for (i = 0; types[i] != NULL; ++i)
|
|
{
|
|
gchar *type = types[i];
|
|
|
|
if (strcmp (type, check_type) == 0)
|
|
{
|
|
result = TRUE;
|
|
break;
|
|
}
|
|
}
|
|
|
|
g_strfreev (types);
|
|
return result;
|
|
}
|
|
|
|
static MetaWindow *
|
|
find_meta_window (MetaCompWindow *comp_window)
|
|
{
|
|
Window xwindow = WS_RESOURCE_XID (comp_window->drawable);
|
|
MetaWindow *window =
|
|
meta_display_lookup_x_window (comp_window->display, xwindow);
|
|
|
|
return window;
|
|
}
|
|
|
|
static void
|
|
send_configure_notify (WsDrawable *drawable)
|
|
{
|
|
WsWindow *window = WS_WINDOW (drawable);
|
|
WsRectangle geo;
|
|
|
|
ws_drawable_query_geometry (drawable, &geo);
|
|
|
|
#if 0
|
|
g_print ("sending configure notify %d %d %d %d\n",
|
|
geo.x, geo.y, geo.width, geo.height);
|
|
#endif
|
|
|
|
ws_window_send_configure_notify (
|
|
window, geo.x, geo.y, geo.width, geo.height,
|
|
0 /* border width */, ws_window_query_override_redirect (window));
|
|
}
|
|
|
|
static WsWindow *
|
|
find_client_window (MetaCompWindow *comp_window)
|
|
{
|
|
MetaWindow *meta_window = find_meta_window (comp_window);
|
|
|
|
if (meta_window && meta_window->frame)
|
|
{
|
|
WsDisplay *ws_display = WS_RESOURCE (comp_window->drawable)->display;
|
|
|
|
#if 0
|
|
g_print ("framed window (client: %lx)\n", WS_RESOURCE_XID (comp_window->drawable));
|
|
#endif
|
|
|
|
#if 0
|
|
g_print ("framed window (client: %lx\n", meta_window->xwindow);
|
|
#endif
|
|
|
|
#if 0
|
|
g_print ("framed window: %p\n", comp_window);
|
|
#endif
|
|
return ws_window_lookup (ws_display, meta_window->xwindow);
|
|
}
|
|
else
|
|
{
|
|
#if 0
|
|
if (meta_window)
|
|
g_print ("window not framed, but managed (%p)\n", comp_window);
|
|
else
|
|
g_print ("no meta window %p\n", comp_window);
|
|
#endif
|
|
|
|
return WS_WINDOW (comp_window->drawable);
|
|
}
|
|
}
|
|
|
|
static gboolean
|
|
private_metacity_window (MetaCompWindow *comp_window)
|
|
{
|
|
/* Returns TRUE if this is a private metacity window
|
|
* such as a tooltip or a menu
|
|
*/
|
|
XID xid = WS_RESOURCE_XID (comp_window->drawable);
|
|
|
|
return meta_ui_window_is_widget (comp_window->screen->ui, xid);
|
|
}
|
|
|
|
static gboolean
|
|
frameless_managed (MetaCompWindow *comp_window)
|
|
{
|
|
/* For some reason frameless, managed windows don't respond to
|
|
* sync requests messages. FIXME: at some point need to find out
|
|
* what's going on
|
|
*/
|
|
MetaWindow *mw = find_meta_window (comp_window);
|
|
|
|
return mw && !mw->frame;
|
|
}
|
|
|
|
static gdouble
|
|
interpolate (gdouble t, gdouble begin, gdouble end, double power)
|
|
{
|
|
return (begin + (end - begin) * pow (t, power));
|
|
}
|
|
|
|
static void
|
|
interpolate_rectangle (gdouble t,
|
|
WsRectangle * from,
|
|
WsRectangle * to,
|
|
WsRectangle * result)
|
|
{
|
|
if (!result)
|
|
return;
|
|
|
|
result->x = interpolate (t, from->x, to->x, 1);
|
|
result->y = interpolate (t, from->y, to->y, 1);
|
|
result->width = interpolate (t, from->width, to->width, 1);
|
|
result->height = interpolate (t, from->height, to->height, 1);
|
|
}
|
|
|
|
static void
|
|
comp_window_set_target_rect (MetaCompWindow *window,
|
|
WsRectangle *rect)
|
|
{
|
|
cm_drawable_node_set_scale_rect (window->node, rect);
|
|
}
|
|
|
|
static void
|
|
comp_window_get_real_size (MetaCompWindow *window,
|
|
WsRectangle *size)
|
|
{
|
|
if (!size)
|
|
return;
|
|
|
|
cm_drawable_node_get_clipbox (window->node, size);
|
|
}
|
|
|
|
#define FADE_TIME 0.225
|
|
|
|
typedef struct
|
|
{
|
|
MetaEffect *effect;
|
|
MetaCompWindow *window;
|
|
GTimer * timer;
|
|
WsRectangle from;
|
|
WsRectangle to;
|
|
gboolean first_time;
|
|
gdouble start_alpha;
|
|
gdouble end_alpha;
|
|
} FadeInfo;
|
|
|
|
static gboolean
|
|
update_fade (gpointer data)
|
|
{
|
|
FadeInfo *info = data;
|
|
gdouble elapsed = g_timer_elapsed (info->timer, NULL);
|
|
gdouble t = elapsed / FADE_TIME;
|
|
|
|
if (elapsed >= FADE_TIME)
|
|
{
|
|
comp_window_set_target_rect (info->window, &info->to);
|
|
cm_drawable_node_set_alpha (info->window->node, info->end_alpha);
|
|
cm_drawable_node_unset_patch (info->window->node);
|
|
comp_window_unref (info->window);
|
|
return FALSE;
|
|
}
|
|
else
|
|
{
|
|
gdouble alpha = interpolate (t, info->start_alpha, info->end_alpha, 1.0);
|
|
WsRectangle cur;
|
|
|
|
if (info->first_time)
|
|
{
|
|
meta_comp_window_show (info->window);
|
|
info->first_time = FALSE;
|
|
}
|
|
|
|
interpolate_rectangle (t, &info->from, &info->to, &cur);
|
|
comp_window_set_target_rect (info->window, &cur);
|
|
cm_drawable_node_set_alpha (info->window->node, alpha);
|
|
return TRUE;
|
|
}
|
|
}
|
|
|
|
static void
|
|
cancel_fade (MetaCompWindow *comp_window)
|
|
{
|
|
if (comp_window->fade_in_idle_id)
|
|
{
|
|
g_source_remove (comp_window->fade_in_idle_id);
|
|
comp_window->fade_in_idle_id = 0;
|
|
}
|
|
}
|
|
|
|
static void
|
|
meta_comp_window_fade_in (MetaCompWindow *comp_window)
|
|
{
|
|
FadeInfo *info = g_new0 (FadeInfo, 1);
|
|
WsWindow *window = find_client_window (comp_window);
|
|
|
|
if (comp_window->fade_in_idle_id)
|
|
return;
|
|
|
|
info->window = comp_window_ref (comp_window);
|
|
info->timer = g_timer_new ();
|
|
|
|
comp_window_get_real_size (info->window, &info->to);
|
|
info->from = info->to;
|
|
|
|
info->start_alpha = 0.1;
|
|
info->first_time = TRUE;
|
|
|
|
if (has_type (window, "_NET_WM_WINDOW_TYPE_DROPDOWN_MENU") ||
|
|
has_type (window, "_NET_WM_WINDOW_TYPE_POPUP_MENU"))
|
|
{
|
|
info->end_alpha = 0.9;
|
|
info->from.width *= 0.6;
|
|
info->from.height *= 0.4;
|
|
}
|
|
else if (has_type (window, "_NET_WM_WINDOW_TYPE_DIALOG"))
|
|
{
|
|
info->end_alpha = 0.9;
|
|
}
|
|
else
|
|
{
|
|
info->end_alpha = 1.0;
|
|
}
|
|
|
|
comp_window->fade_in_idle_id = g_idle_add (update_fade, info);
|
|
}
|
|
|
|
static void
|
|
on_request_alarm (WsSyncAlarm *alarm,
|
|
WsAlarmNotifyEvent *event,
|
|
MetaCompWindow *comp_window)
|
|
{
|
|
/* This alarm means that the window is ready to be shown on screen */
|
|
|
|
if (comp_window->unminimize_info)
|
|
start_unminimize (comp_window);
|
|
else
|
|
meta_comp_window_fade_in (comp_window);
|
|
|
|
g_object_unref (alarm);
|
|
}
|
|
|
|
static gboolean
|
|
send_sync_request (MetaCompWindow *comp_window)
|
|
{
|
|
WsDisplay *display;
|
|
WsWindow *client_window = find_client_window (comp_window);
|
|
WsSyncCounter *request_counter;
|
|
WsSyncAlarm *alarm;
|
|
guint32 msg[5];
|
|
display = WS_RESOURCE (comp_window->drawable)->display;
|
|
ws_display_init_sync (display);
|
|
|
|
if (!client_window)
|
|
return FALSE;
|
|
|
|
request_counter = ws_window_get_property_sync_counter (
|
|
client_window, "_NET_WM_SYNC_REQUEST_COUNTER");
|
|
|
|
if (!request_counter)
|
|
return FALSE;
|
|
|
|
comp_window->counter_value = ws_sync_counter_query_value (request_counter) + 1;
|
|
|
|
msg[0] = comp_window->display->atom_net_wm_sync_request;
|
|
msg[1] = meta_display_get_current_time (comp_window->display);
|
|
msg[2] = comp_window->counter_value & 0xffffffff;
|
|
msg[3] = (comp_window->counter_value >> 32) & 0xffffffff;
|
|
|
|
alarm = ws_sync_alarm_new (display, request_counter);
|
|
|
|
ws_sync_alarm_set (alarm, comp_window->counter_value);
|
|
|
|
g_signal_connect (alarm, "alarm_notify_event",
|
|
G_CALLBACK (on_request_alarm), comp_window);
|
|
|
|
ws_window_send_client_message (client_window,
|
|
"WM_PROTOCOLS", msg);
|
|
|
|
send_configure_notify (WS_DRAWABLE (client_window));
|
|
|
|
ws_display_flush (WS_RESOURCE (client_window)->display);
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
void
|
|
meta_comp_window_refresh_attrs (MetaCompWindow *comp_window)
|
|
{
|
|
/* FIXME: this function should not exist - the real problem is
|
|
* probably in meta_screen_info_add_window() where it it called.
|
|
*/
|
|
|
|
double alpha = 1.0;
|
|
CmDrawableNode *node = CM_DRAWABLE_NODE (comp_window->node);
|
|
|
|
#if 0
|
|
g_print ("waiting for paint: %d\n", comp_window->waiting_for_paint);
|
|
#endif
|
|
|
|
if (ws_window_query_mapped (WS_WINDOW (comp_window->drawable))
|
|
#if 0
|
|
&& !comp_window->waiting_for_paint
|
|
#endif
|
|
)
|
|
{
|
|
WsWindow *window = WS_WINDOW (comp_window->drawable);
|
|
|
|
cm_drawable_node_unset_patch (CM_DRAWABLE_NODE (node));
|
|
|
|
if (has_type (window, "_NET_WM_WINDOW_TYPE_DROPDOWN_MENU"))
|
|
{
|
|
alpha = 0.9;
|
|
}
|
|
else if (has_type (window, "_NET_WM_WINDOW_TYPE_POPUP_MENU"))
|
|
{
|
|
alpha = 0.9;
|
|
}
|
|
else
|
|
{
|
|
alpha = 1.0;
|
|
}
|
|
|
|
cm_drawable_node_set_alpha (node, alpha);
|
|
|
|
if (!cm_drawable_node_get_viewable (node))
|
|
{
|
|
comp_window->waiting_for_paint = TRUE;
|
|
#if 0
|
|
alarm = ws_alarm_new (comp_window->display);
|
|
#endif
|
|
#if 0
|
|
finish_counter = ws_window_get_property_sync_counter (
|
|
window, "_NET_WM_FINISH_FRAME_COUNTER");
|
|
#endif
|
|
|
|
/* For some reason the panel and nautilus don't respond to the
|
|
* sync counter stuff. FIXME: this should be figured out at
|
|
* some point.
|
|
*/
|
|
if (frameless_managed (comp_window) ||
|
|
private_metacity_window (comp_window) ||
|
|
!send_sync_request (comp_window))
|
|
{
|
|
meta_comp_window_show (comp_window);
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
#if 0
|
|
g_print ("unmapping %p\n", node);
|
|
#endif
|
|
|
|
meta_comp_window_hide (comp_window);
|
|
}
|
|
}
|
|
|
|
void
|
|
meta_comp_window_set_updates (MetaCompWindow *comp_window,
|
|
gboolean updates)
|
|
{
|
|
CmDrawableNode *node = CM_DRAWABLE_NODE (comp_window->node);
|
|
|
|
comp_window->updates = updates;
|
|
|
|
cm_drawable_node_set_updates (node, updates);
|
|
|
|
if (updates)
|
|
{
|
|
WsRectangle rect;
|
|
WsRegion *shape;
|
|
WsDisplay *display = WS_RESOURCE (node->drawable)->display;
|
|
|
|
ws_display_begin_error_trap (display);
|
|
ws_drawable_query_geometry (node->drawable, &rect);
|
|
cm_drawable_node_update_pixmap (node);
|
|
cm_drawable_node_set_geometry (node, &rect);
|
|
shape = ws_window_get_output_shape (WS_WINDOW (node->drawable));
|
|
cm_drawable_node_set_shape (node, shape);
|
|
ws_region_destroy (shape);
|
|
ws_display_end_error_trap (display);
|
|
}
|
|
}
|
|
|
|
CmNode *
|
|
meta_comp_window_get_node (MetaCompWindow *comp_window)
|
|
{
|
|
return CM_NODE (comp_window->node);
|
|
}
|
|
|
|
/*
|
|
* Explosion effect
|
|
*/
|
|
#define EXPLODE_TIME 1.0
|
|
|
|
#define BASE 0.5
|
|
|
|
static double
|
|
transform (double in)
|
|
{
|
|
return (pow (BASE, in) - 1) / (BASE - 1);
|
|
}
|
|
|
|
typedef struct
|
|
{
|
|
MetaEffect *effect;
|
|
MetaCompWindow *comp_window;
|
|
gdouble level;
|
|
GTimer * timer;
|
|
} ExplodeInfo;
|
|
|
|
static gboolean
|
|
update_explosion (gpointer data)
|
|
{
|
|
ExplodeInfo *info = data;
|
|
CmDrawableNode *node = CM_DRAWABLE_NODE (info->comp_window->node);
|
|
gdouble elapsed = g_timer_elapsed (info->timer, NULL);
|
|
|
|
if (!cm_drawable_node_get_viewable (node) || elapsed > EXPLODE_TIME)
|
|
{
|
|
meta_effect_end (info->effect);
|
|
|
|
info->comp_window->animation_in_progress = FALSE;
|
|
if (info->comp_window->hide_after_animation)
|
|
meta_comp_window_hide (info->comp_window);
|
|
|
|
cm_drawable_node_set_explosion_level (node, 0.0);
|
|
|
|
comp_window_unref (info->comp_window);
|
|
return FALSE;
|
|
}
|
|
else
|
|
{
|
|
gdouble t = elapsed / EXPLODE_TIME;
|
|
|
|
cm_drawable_node_set_explosion_level (
|
|
node, transform (t));
|
|
return TRUE;
|
|
}
|
|
}
|
|
|
|
void
|
|
meta_comp_window_explode (MetaCompWindow *comp_window,
|
|
MetaEffect *effect)
|
|
{
|
|
ExplodeInfo *info = g_new0 (ExplodeInfo, 1);
|
|
|
|
if (!cm_drawable_node_get_viewable (comp_window->node))
|
|
return;
|
|
|
|
comp_window->animation_in_progress = TRUE;
|
|
|
|
info->comp_window = comp_window_ref (comp_window);
|
|
info->effect = effect;
|
|
info->level = 0.0;
|
|
info->timer = g_timer_new ();
|
|
|
|
g_idle_add (update_explosion, info);
|
|
}
|
|
|
|
/* shrinkydink minimize effect */
|
|
|
|
#define N_PHASES 5
|
|
|
|
typedef struct
|
|
{
|
|
WsRectangle start_rect;
|
|
WsRectangle end_rect;
|
|
gdouble start_alpha;
|
|
gdouble end_alpha;
|
|
gdouble start_time;
|
|
gdouble end_time;
|
|
} Phase;
|
|
|
|
typedef struct
|
|
{
|
|
GTimer *timer;
|
|
|
|
MetaCompWindow *comp_window;
|
|
|
|
MetaEffect *effect;
|
|
|
|
Phase phases[N_PHASES];
|
|
} MiniInfo;
|
|
|
|
static void
|
|
set_geometry (MetaCompWindow *window,
|
|
Phase *phase,
|
|
gdouble elapsed)
|
|
{
|
|
WsRectangle rect;
|
|
gdouble alpha;
|
|
gdouble t = (elapsed - phase->start_time) / (phase->end_time - phase->start_time);
|
|
|
|
interpolate_rectangle (t, &phase->start_rect, &phase->end_rect, &rect);
|
|
alpha = interpolate (t, phase->start_alpha, phase->end_alpha, 1.0);
|
|
|
|
comp_window_set_target_rect (window, &rect);
|
|
cm_drawable_node_set_alpha (window->node, alpha);
|
|
}
|
|
|
|
static int
|
|
center (gdouble what, gdouble in)
|
|
{
|
|
return (in - what) / 2.0 + 0.5;
|
|
}
|
|
|
|
#define WOBBLE_FACTOR 1.5
|
|
|
|
static void
|
|
generate_phases (WsRectangle *start,
|
|
WsRectangle *icon,
|
|
Phase phases[N_PHASES])
|
|
{
|
|
const double phase_times[5] = {
|
|
0.225, /* scale to size of button */
|
|
0.100, /* scale up a little */
|
|
0.100, /* scale back a little */
|
|
0.100, /* drop down to icon */
|
|
0.350, /* fade out */
|
|
};
|
|
|
|
WsRectangle cur;
|
|
gdouble alpha;
|
|
gdouble aspect_ratio;
|
|
gdouble time;
|
|
int i;
|
|
|
|
aspect_ratio = (double)start->width / (double)start->height;
|
|
cur = *start;
|
|
time = 0.0;
|
|
alpha = 1.0;
|
|
for (i = 0; i < N_PHASES; ++i)
|
|
{
|
|
Phase *phase = &(phases[i]);
|
|
WsRectangle *end = &(phase->end_rect);
|
|
|
|
phase->start_time = time;
|
|
phase->start_rect = cur;
|
|
phase->start_alpha = alpha;
|
|
phase->end_alpha = 1.0;
|
|
phase->end_time = time + phase_times[i];
|
|
|
|
if (i == 0)
|
|
{
|
|
/* Shrink to a little rectangle */
|
|
end->height = icon->height;
|
|
end->width = icon->height * aspect_ratio;
|
|
#if 0
|
|
end->x = icon->x + center (end->width, icon->width);
|
|
#endif
|
|
end->x = cur.x + center (end->width, cur.width);
|
|
end->y = cur.y + center (icon->height, cur.height);
|
|
}
|
|
else if (i == 1)
|
|
{
|
|
/* Zoom out a little */
|
|
end->x = cur.x + center (WOBBLE_FACTOR * cur.width, cur.width);
|
|
end->y = cur.y + center (WOBBLE_FACTOR * cur.height, cur.height);
|
|
end->width = cur.width * WOBBLE_FACTOR;
|
|
end->height = cur.height * WOBBLE_FACTOR;
|
|
}
|
|
else if (i == 2)
|
|
{
|
|
/* Zoom back */
|
|
end->height = icon->height;
|
|
end->width = icon->height * aspect_ratio;
|
|
#if 0
|
|
end->x = icon->x + center (end->width, icon->width);
|
|
#endif
|
|
end->x = cur.x + center (end->width, cur.width);
|
|
end->y = cur.y + center (icon->height, cur.height);
|
|
}
|
|
else if (i == 3)
|
|
{
|
|
/* Move down to the button */
|
|
end->height = icon->height;
|
|
end->width = icon->height * aspect_ratio;
|
|
end->x = icon->x + center (end->width, icon->width);
|
|
end->y = icon->y;
|
|
}
|
|
else if (i == 4)
|
|
{
|
|
/* Fade out */
|
|
end->x = icon->x;
|
|
end->y = icon->y;
|
|
end->width = icon->width;
|
|
end->height = icon->height;
|
|
|
|
phases[i].end_alpha = 0.0;
|
|
}
|
|
|
|
alpha = phase->end_alpha;
|
|
cur = phase->end_rect;
|
|
time += phase_times[i];
|
|
}
|
|
}
|
|
|
|
static gboolean
|
|
update_minimize (gpointer data)
|
|
{
|
|
MiniInfo *info = data;
|
|
Phase *current_phase;
|
|
int i;
|
|
gdouble elapsed = g_timer_elapsed (info->timer, NULL);
|
|
|
|
current_phase = NULL;
|
|
for (i = 0; i < N_PHASES; ++i)
|
|
{
|
|
Phase *p = &(info->phases[i]);
|
|
|
|
if (p->start_time < elapsed && p->end_time >= elapsed)
|
|
{
|
|
current_phase = p;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (current_phase)
|
|
{
|
|
set_geometry (info->comp_window, current_phase, elapsed);
|
|
return TRUE;
|
|
}
|
|
else
|
|
{
|
|
meta_comp_window_hide (info->comp_window);
|
|
cm_drawable_node_set_alpha (info->comp_window->node, 1.0);
|
|
cm_drawable_node_unset_patch (info->comp_window->node);
|
|
comp_window_unref (info->comp_window);
|
|
|
|
meta_effect_end (info->effect);
|
|
|
|
return FALSE;
|
|
}
|
|
}
|
|
|
|
static void
|
|
meta_rect_to_ws_rect (MetaRectangle *mrect,
|
|
WsRectangle *wrect)
|
|
{
|
|
if (!mrect || !wrect)
|
|
return;
|
|
|
|
wrect->x = mrect->x;
|
|
wrect->y = mrect->y;
|
|
wrect->width = mrect->width;
|
|
wrect->height = mrect->height;
|
|
}
|
|
|
|
void
|
|
meta_comp_window_run_minimize (MetaCompWindow *window,
|
|
MetaEffect *effect)
|
|
{
|
|
MiniInfo *info = g_new (MiniInfo, 1);
|
|
WsRectangle start, end;
|
|
|
|
info->timer = g_timer_new ();
|
|
|
|
info->comp_window = comp_window_ref (window);
|
|
|
|
info->effect = effect;
|
|
|
|
meta_rect_to_ws_rect (&(effect->u.minimize.window_rect), &start);
|
|
meta_rect_to_ws_rect (&(effect->u.minimize.icon_rect), &end);
|
|
|
|
generate_phases (&start, &end, info->phases);
|
|
|
|
g_idle_add (update_minimize, info);
|
|
}
|
|
|
|
struct UnminimizeInfo
|
|
{
|
|
GTimer *timer;
|
|
|
|
MetaCompWindow *comp_window;
|
|
|
|
Phase phases[N_PHASES];
|
|
gboolean first_time;
|
|
};
|
|
|
|
#define WOBBLE_FACTOR_OUT 0.7
|
|
|
|
static void
|
|
generate_unminimize_phases (WsRectangle *icon,
|
|
WsRectangle *full,
|
|
Phase phases[N_PHASES])
|
|
{
|
|
const double phase_times[5] = {
|
|
0.350, /* fade in */
|
|
0.100, /* move up from icon */
|
|
0.225, /* scale to full size */
|
|
0.100, /* scale down a little */
|
|
0.100, /* scale to full size */
|
|
};
|
|
|
|
WsRectangle cur;
|
|
gdouble aspect_ratio;
|
|
gdouble time;
|
|
int i;
|
|
|
|
aspect_ratio = (double)full->width / (double)full->height;
|
|
cur = *icon;
|
|
time = 0.0;
|
|
for (i = 0; i < N_PHASES; ++i)
|
|
{
|
|
Phase *phase = &(phases[i]);
|
|
WsRectangle *end = &(phase->end_rect);
|
|
|
|
phase->start_time = time;
|
|
phase->start_rect = cur;
|
|
phase->start_alpha = 1.0;
|
|
phase->end_alpha = 1.0;
|
|
phase->end_time = time + phase_times[i];
|
|
|
|
if (i == 0)
|
|
{
|
|
/* Fade in */
|
|
phase->end_alpha = 1.0;
|
|
phase->start_alpha = 0.0;
|
|
end->height = icon->height;
|
|
end->width = icon->height * aspect_ratio;
|
|
end->x = icon->x + center (end->width, icon->width);
|
|
end->y = icon->y;
|
|
}
|
|
else if (i == 1)
|
|
{
|
|
/* Move up from icon */
|
|
end->width = cur.width;
|
|
end->height = cur.height;
|
|
#if 0
|
|
end->x = cur.x;
|
|
#endif
|
|
end->x = full->x + center (end->width, full->width);
|
|
end->y = full->y + center (icon->height, full->height);
|
|
}
|
|
else if (i == 2)
|
|
{
|
|
/* Zoom to full size */
|
|
*end = *full;
|
|
}
|
|
else if (i == 3)
|
|
{
|
|
/* Scale down a little */
|
|
end->x = cur.x + center (cur.width * WOBBLE_FACTOR_OUT, cur.width);
|
|
end->y = cur.y + center (cur.height * WOBBLE_FACTOR_OUT, cur.height);
|
|
end->width = cur.width * WOBBLE_FACTOR_OUT;
|
|
end->height = cur.height * WOBBLE_FACTOR_OUT;
|
|
}
|
|
else if (i == 4)
|
|
{
|
|
/* Scale up to full size again */
|
|
*end = *full;
|
|
}
|
|
|
|
cur = phase->end_rect;
|
|
time += phase_times[i];
|
|
}
|
|
}
|
|
|
|
static gboolean
|
|
update_unminimize (gpointer data)
|
|
{
|
|
UnminimizeInfo *info = data;
|
|
Phase *current_phase;
|
|
int i;
|
|
gdouble elapsed = g_timer_elapsed (info->timer, NULL);
|
|
|
|
current_phase = NULL;
|
|
for (i = 0; i < N_PHASES; ++i)
|
|
{
|
|
Phase *p = &(info->phases[i]);
|
|
|
|
if (p->start_time < elapsed && p->end_time >= elapsed)
|
|
{
|
|
current_phase = p;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (current_phase)
|
|
{
|
|
if (info->first_time)
|
|
{
|
|
meta_comp_window_show (info->comp_window);
|
|
info->first_time = FALSE;
|
|
}
|
|
|
|
set_geometry (info->comp_window, current_phase, elapsed);
|
|
return TRUE;
|
|
}
|
|
else
|
|
{
|
|
cm_drawable_node_set_alpha (info->comp_window->node, 1.0);
|
|
cm_drawable_node_unset_patch (info->comp_window->node);
|
|
comp_window_unref (info->comp_window);
|
|
#if 0
|
|
g_print ("done\n");
|
|
#endif
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
}
|
|
|
|
static void
|
|
start_unminimize (MetaCompWindow *comp_window)
|
|
{
|
|
UnminimizeInfo *info = comp_window->unminimize_info;
|
|
|
|
if (!info)
|
|
return;
|
|
|
|
comp_window->unminimize_info = NULL;
|
|
|
|
info->timer = g_timer_new ();
|
|
info->first_time = TRUE;
|
|
|
|
g_idle_add (update_unminimize, info);
|
|
}
|
|
|
|
void
|
|
meta_comp_window_run_unminimize (MetaCompWindow *comp_window,
|
|
MetaEffect *effect)
|
|
{
|
|
WsRectangle start, end;
|
|
UnminimizeInfo *info = g_new0 (UnminimizeInfo, 1);
|
|
|
|
meta_rect_to_ws_rect (&(effect->u.unminimize.icon_rect), &start);
|
|
meta_rect_to_ws_rect (&(effect->u.unminimize.window_rect), &end);
|
|
|
|
generate_unminimize_phases (&start, &end, info->phases);
|
|
|
|
info->comp_window = comp_window_ref (comp_window);
|
|
|
|
comp_window->unminimize_info = info;
|
|
|
|
meta_effect_end (effect);
|
|
}
|
|
|
|
/* bounce effect */
|
|
|
|
typedef struct
|
|
{
|
|
MetaEffect *effect;
|
|
MetaCompWindow *window;
|
|
GTimer *timer;
|
|
Model *model;
|
|
MetaRectangle rect;
|
|
gdouble last_time;
|
|
} FocusInfo;
|
|
|
|
/* XXX HATE */
|
|
extern void get_patch_points (Model *model, CmPoint points[4][4]);
|
|
extern void compute_window_rect (MetaWindow *window, MetaRectangle *rect);
|
|
|
|
static gboolean
|
|
update_focus (gpointer data)
|
|
{
|
|
FocusInfo *info = data;
|
|
CmDrawableNode *node = (CmDrawableNode *)info->window->node;
|
|
gdouble elapsed = g_timer_elapsed (info->timer, NULL);
|
|
int i;
|
|
int n_steps = floor ((elapsed - info->last_time) * 60);
|
|
CmPoint points[4][4];
|
|
|
|
if (model_is_calm (info->model) || elapsed > 0.7)
|
|
{
|
|
cm_drawable_node_unset_patch (node);
|
|
meta_effect_end (info->effect);
|
|
g_free(info);
|
|
return FALSE;
|
|
}
|
|
|
|
for (i = 0; i < n_steps; ++i)
|
|
model_step (info->model);
|
|
|
|
if (i > 0)
|
|
info->last_time = elapsed;
|
|
|
|
get_patch_points (info->model, points);
|
|
|
|
cm_drawable_node_set_patch (node, points);
|
|
return TRUE;
|
|
}
|
|
|
|
void
|
|
meta_comp_window_run_focus (MetaCompWindow *comp_window,
|
|
MetaEffect *effect)
|
|
{
|
|
FocusInfo *info = g_new0 (FocusInfo, 1);
|
|
MetaWindow *meta_window =
|
|
meta_display_lookup_x_window (comp_window->display,
|
|
WS_RESOURCE_XID (comp_window->drawable));
|
|
|
|
info->window = comp_window;
|
|
info->effect = effect;
|
|
info->timer = g_timer_new ();
|
|
info->last_time = 0;
|
|
|
|
compute_window_rect (meta_window, &info->rect);
|
|
info->model = model_new (&info->rect, TRUE);
|
|
|
|
g_idle_add (update_focus, info);
|
|
}
|
|
|
|
void
|
|
meta_comp_window_freeze_stack (MetaCompWindow *comp_window)
|
|
{
|
|
comp_window->stack_freeze_count++;
|
|
}
|
|
|
|
void
|
|
meta_comp_window_thaw_stack (MetaCompWindow *comp_window)
|
|
{
|
|
comp_window->stack_freeze_count--;
|
|
}
|
|
|
|
gboolean
|
|
meta_comp_window_stack_frozen (MetaCompWindow *comp_window)
|
|
{
|
|
return comp_window->stack_freeze_count > 0;
|
|
}
|
|
|
|
#endif
|
|
|