mutter/src/c-window.c
Elijah Newren 0201fcfc6c Stick an emacs comment directive at the beginning of all the code files so
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
2006-10-01 22:30:10 +00:00

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