diff --git a/ChangeLog b/ChangeLog index 5a451839b..48ee136c6 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,9 @@ +2003-11-23 Havoc Pennington + + * src/compositor.c: move xcompmgr code in here (minus drop + shadows), untested since Keith's server just crashes at the + moment. "It compiles" + 2003-11-20 Havoc Pennington * src/window.c (meta_window_new_with_attrs): new function diff --git a/src/compositor.c b/src/compositor.c index e9aef849f..05323b972 100644 --- a/src/compositor.c +++ b/src/compositor.c @@ -2,6 +2,7 @@ /* * Copyright (C) 2003 Red Hat, Inc. + * Copyright (C) 2003 Keith Packard * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as @@ -21,13 +22,14 @@ #include #include "compositor.h" +#include "screen.h" +#include "errors.h" #ifdef HAVE_COMPOSITE_EXTENSIONS #include #include #include - #endif /* HAVE_COMPOSITE_EXTENSIONS */ /* Unlike MetaWindow, there's one of these for _all_ toplevel windows, @@ -40,17 +42,30 @@ typedef struct { Window xwindow; + +#ifdef HAVE_COMPOSITE_EXTENSIONS + MetaCompositor *compositor; + int x; + int y; + int width; + int height; + int border_width; + Damage damage; + XserverRegion last_painted_extents; + Picture picture; + XserverRegion border_size; + + unsigned int managed : 1; + unsigned int damaged : 1; + + unsigned int screen_index : 8; + +#endif } MetaCompositorWindow; -typedef struct -{ - GList *windows; - -} MetaCompositorScreen; - struct MetaCompositor { MetaDisplay *display; @@ -62,13 +77,36 @@ struct MetaCompositor int fixes_error_base; int fixes_event_base; int render_error_base; - int render_event_base; + int render_event_base; + + GHashTable *window_hash; - GSList *screens; + guint repair_idle; guint enabled : 1; }; +#ifdef HAVE_COMPOSITE_EXTENSIONS +static void +meta_compositor_window_free (MetaCompositorWindow *cwindow) +{ + XDamageDestroy (cwindow->compositor->display->xdisplay, + cwindow->damage); + + g_free (cwindow); +} +#endif /* HAVE_COMPOSITE_EXTENSIONS */ + +#ifdef HAVE_COMPOSITE_EXTENSIONS +static void +free_window_hash_value (void *v) +{ + MetaCompositorWindow *cwindow = v; + + meta_compositor_window_free (cwindow); +} +#endif /* HAVE_COMPOSITE_EXTENSIONS */ + MetaCompositor* meta_compositor_new (MetaDisplay *display) { @@ -137,6 +175,11 @@ meta_compositor_new (MetaDisplay *display) return compositor; } + compositor->window_hash = g_hash_table_new_full (meta_unsigned_long_hash, + meta_unsigned_long_equal, + NULL, + free_window_hash_value); + compositor->enabled = TRUE; return compositor; @@ -145,6 +188,18 @@ meta_compositor_new (MetaDisplay *display) #endif /* HAVE_COMPOSITE_EXTENSIONS */ } +#ifdef HAVE_COMPOSITE_EXTENSIONS +static void +remove_repair_idle (MetaCompositor *compositor) +{ + if (compositor->repair_idle != 0) + { + g_source_remove (compositor->repair_idle); + compositor->repair_idle = 0; + } +} +#endif /* HAVE_COMPOSITE_EXTENSIONS */ + void meta_compositor_unref (MetaCompositor *compositor) { @@ -152,27 +207,385 @@ meta_compositor_unref (MetaCompositor *compositor) /* There isn't really a refcount at the moment since * there's no ref() */ + remove_repair_idle (compositor); + + g_hash_table_destroy (compositor->window_hash); g_free (compositor); #endif /* HAVE_COMPOSITE_EXTENSIONS */ } +#ifdef HAVE_COMPOSITE_EXTENSIONS +static XserverRegion +window_extents (MetaCompositorWindow *cwindow) +{ + XRectangle r; + + r.x = cwindow->x; + r.y = cwindow->y; + r.width = cwindow->width; + r.height = cwindow->height; + + return XFixesCreateRegion (cwindow->compositor->display->xdisplay, &r, 1); +} +#endif /* HAVE_COMPOSITE_EXTENSIONS */ + +#ifdef HAVE_COMPOSITE_EXTENSIONS +static void +paint_screen (MetaCompositor *compositor, + MetaScreen *screen, + XserverRegion damage_region) +{ + XserverRegion region; + Picture buffer_picture; + Pixmap buffer_pixmap; + Display *xdisplay; + XRenderPictFormat *format; + GList *tmp; + + xdisplay = screen->display->xdisplay; + + if (damage_region == None) + { + XRectangle r; + + r.x = 0; + r.y = 0; + r.width = screen->width; + r.height = screen->height; + + region = XFixesCreateRegion (xdisplay, &r, 1); + } + else + { + region = XFixesCreateRegion (xdisplay, NULL, 0); + + XFixesCopyRegion (compositor->display->xdisplay, + region, + damage_region); + } + + buffer_pixmap = XCreatePixmap (xdisplay, None, + screen->width, + screen->height, + DefaultDepth (xdisplay, + screen->number)); + + format = XRenderFindVisualFormat (xdisplay, + DefaultVisual (xdisplay, + screen->number)); + + buffer_picture = XRenderCreatePicture (xdisplay, + buffer_pixmap, + format, + 0, 0); + + /* set clip on the root window */ + XFixesSetPictureClipRegion (xdisplay, + screen->root_picture, 0, 0, region); + + /* draw windows from bottom to top */ + meta_error_trap_push (compositor->display); + tmp = g_list_last (screen->compositor_windows); + while (tmp != NULL) + { + MetaCompositorWindow *cwindow = tmp->data; + + if (cwindow->picture == None) /* InputOnly */ + goto next; + + if (cwindow->last_painted_extents) + XFixesDestroyRegion (xdisplay, + cwindow->last_painted_extents); + + cwindow->last_painted_extents = window_extents (cwindow); + + XFixesSetPictureClipRegion (xdisplay, + buffer_picture, 0, 0, + region); + + /* XFixesSubtractRegion (dpy, region, region, 0, 0, w->borderSize, 0, 0); */ + + XRenderComposite (xdisplay, + PictOpSrc, /* PictOpOver for alpha */ + cwindow->picture, + None, buffer_picture, + 0, 0, 0, 0, + cwindow->x + cwindow->border_width, + cwindow->y + cwindow->border_width, + cwindow->width, + cwindow->height); + + next: + tmp = tmp->prev; + } + meta_error_trap_pop (compositor->display, FALSE); + + /* Copy buffer to root window */ + + XFixesSetPictureClipRegion (xdisplay, buffer_picture, 0, 0, None); + XRenderComposite (xdisplay, PictOpSrc, buffer_picture, None, + screen->root_picture, + 0, 0, 0, 0, 0, 0, + screen->width, screen->height); + + XFixesDestroyRegion (xdisplay, region); + XFreePixmap (xdisplay, buffer_pixmap); + XRenderFreePicture (xdisplay, buffer_picture); +} +#endif /* HAVE_COMPOSITE_EXTENSIONS */ + +#ifdef HAVE_COMPOSITE_EXTENSIONS +static gboolean +repair_idle_func (void *data) +{ + GSList *tmp; + + MetaCompositor *compositor = data; + + tmp = compositor->display->screens; + while (tmp != NULL) + { + MetaScreen *s = tmp->data; + + if (s->damage_region != None) + { + paint_screen (compositor, s, + s->damage_region); + XFixesDestroyRegion (s->display->xdisplay, + s->damage_region); + s->damage_region = None; + } + + tmp = tmp->next; + } + + compositor->repair_idle = 0; + + return FALSE; +} +#endif /* HAVE_COMPOSITE_EXTENSIONS */ + +#ifdef HAVE_COMPOSITE_EXTENSIONS +static MetaScreen* +meta_compositor_window_get_screen (MetaCompositorWindow *cwindow) +{ + MetaScreen *screen; + GSList *tmp; + + screen = NULL; + tmp = cwindow->compositor->display->screens; + while (tmp != NULL) + { + MetaScreen *s = tmp->data; + + if (s->number == cwindow->screen_index) + { + screen = s; + break; + } + + tmp = tmp->next; + } + g_assert (screen != NULL); + + return screen; +} +#endif /* HAVE_COMPOSITE_EXTENSIONS */ + +#ifdef HAVE_COMPOSITE_EXTENSIONS +static void +ensure_repair_idle (MetaCompositor *compositor) +{ + if (compositor->repair_idle != 0) + return; + + compositor->repair_idle = g_idle_add (repair_idle_func, compositor); +} +#endif /* HAVE_COMPOSITE_EXTENSIONS */ + +#ifdef HAVE_COMPOSITE_EXTENSIONS +static void +merge_and_destroy_damage_region (MetaCompositor *compositor, + MetaScreen *screen, + XserverRegion region) +{ + if (screen->damage_region != None) + { + XFixesCopyRegion (compositor->display->xdisplay, + screen->damage_region, + region); + XFixesDestroyRegion (compositor->display->xdisplay, + region); + } + else + { + screen->damage_region = region; + } + + ensure_repair_idle (compositor); +} +#endif /* HAVE_COMPOSITE_EXTENSIONS */ + +#ifdef HAVE_COMPOSITE_EXTENSIONS +static void +merge_damage_region (MetaCompositor *compositor, + MetaScreen *screen, + XserverRegion region) +{ + if (screen->damage_region == None) + screen->damage_region = + XFixesCreateRegion (compositor->display->xdisplay, NULL, 0); + + XFixesCopyRegion (compositor->display->xdisplay, + screen->damage_region, + region); + + ensure_repair_idle (compositor); +} +#endif /* HAVE_COMPOSITE_EXTENSIONS */ + +#ifdef HAVE_COMPOSITE_EXTENSIONS +static void +process_damage_notify (MetaCompositor *compositor, + XDamageNotifyEvent *event) +{ + MetaCompositorWindow *cwindow; + XserverRegion region; + MetaScreen *screen; + + cwindow = g_hash_table_lookup (compositor->window_hash, + &event->drawable); + if (cwindow == NULL) + return; + + region = XFixesCreateRegion (compositor->display->xdisplay, NULL, 0); + + /* translate region to screen */ + XDamageSubtract (compositor->display->xdisplay, + cwindow->damage, None, region); + + XFixesTranslateRegion (compositor->display->xdisplay, + region, + cwindow->x, + cwindow->y); + + screen = meta_compositor_window_get_screen (cwindow); + + merge_and_destroy_damage_region (compositor, screen, region); +} +#endif /* HAVE_COMPOSITE_EXTENSIONS */ + +#ifdef HAVE_COMPOSITE_EXTENSIONS +static void +process_configure_notify (MetaCompositor *compositor, + XConfigureEvent *event) +{ + MetaCompositorWindow *cwindow; + MetaScreen *screen; + GList *link; + Window above; + XserverRegion region; + + cwindow = g_hash_table_lookup (compositor->window_hash, + &event->window); + if (cwindow == NULL) + return; + + screen = meta_compositor_window_get_screen (cwindow); + + if (cwindow->last_painted_extents) + { + merge_damage_region (compositor, + screen, + cwindow->last_painted_extents); + } + + cwindow->x = event->x; + cwindow->y = event->y; + cwindow->width = event->width; + cwindow->height = event->height; + cwindow->border_width = event->border_width; + + link = g_list_find (screen->compositor_windows, + cwindow); + + g_assert (link != NULL); + + if (link->next) + above = ((MetaCompositorWindow*) link->next)->xwindow; + else + above = None; + + if (above != event->above) + { + GList *tmp; + + screen->compositor_windows = + g_list_delete_link (screen->compositor_windows, + link); + link = NULL; + + /* Note that event->above is None if our window is on the bottom */ + tmp = screen->compositor_windows; + while (tmp != NULL) + { + MetaCompositorWindow *t = tmp->data; + + if (t->xwindow == event->above) + { + /* We are above this window, i.e. earlier in list */ + break; + } + + tmp = tmp->next; + } + + if (tmp != NULL) + { + screen->compositor_windows = + g_list_insert_before (screen->compositor_windows, + tmp, + cwindow); + } + else + screen->compositor_windows = + g_list_prepend (screen->compositor_windows, + cwindow); + } + + region = window_extents (cwindow); + merge_damage_region (compositor, + screen, + region); + XFixesDestroyRegion (compositor->display->xdisplay, region); +} +#endif /* HAVE_COMPOSITE_EXTENSIONS */ + void meta_compositor_process_event (MetaCompositor *compositor, - XEvent *xevent, + XEvent *event, MetaWindow *window) { #ifdef HAVE_COMPOSITE_EXTENSIONS if (!compositor->enabled) return; /* no extension */ - - + if (event->type == (compositor->damage_event_base + XDamageNotify)) + { + process_damage_notify (compositor, + (XDamageNotifyEvent*) event); + } + else if (event->type == ConfigureNotify) + { + process_configure_notify (compositor, + (XConfigureEvent*) event); + } #endif /* HAVE_COMPOSITE_EXTENSIONS */ } /* This is called when metacity does its XQueryTree() on startup - * and when a new window is created. + * and when a new window is mapped. */ void meta_compositor_add_window (MetaCompositor *compositor, @@ -180,11 +593,71 @@ meta_compositor_add_window (MetaCompositor *compositor, XWindowAttributes *attrs) { #ifdef HAVE_COMPOSITE_EXTENSIONS + MetaCompositorWindow *cwindow; + MetaScreen *screen; + Damage damage; + XRenderPictFormat *format; + XRenderPictureAttributes pa; + g_print ("compositor adding window 0x%lx\n", xwindow); if (!compositor->enabled) return; /* no extension */ + screen = meta_screen_for_x_screen (attrs->screen); + g_assert (screen != NULL); + + cwindow = g_hash_table_lookup (compositor->window_hash, + &xwindow); + + if (cwindow != NULL) + return; + + /* Create Damage object to monitor window damage */ + meta_error_trap_push (compositor->display); + damage = XDamageCreate (compositor->display->xdisplay, + xwindow, XDamageReportNonEmpty); + meta_error_trap_pop (compositor->display, FALSE); + + if (damage == None) + return; + + cwindow = g_new0 (MetaCompositorWindow, 1); + + cwindow->compositor = compositor; + cwindow->xwindow = xwindow; + cwindow->screen_index = screen->number; + cwindow->damage = damage; + cwindow->x = attrs->x; + cwindow->y = attrs->y; + cwindow->width = attrs->width; + cwindow->height = attrs->height; + cwindow->border_width = attrs->border_width; + + pa.subwindow_mode = IncludeInferiors; + + if (attrs->class != InputOnly) + { + format = XRenderFindVisualFormat (compositor->display->xdisplay, + attrs->visual); + cwindow->picture = XRenderCreatePicture (compositor->display->xdisplay, + xwindow, + format, + CPSubwindowMode, + &pa); + } + else + { + cwindow->picture = None; + } + + g_hash_table_insert (compositor->window_hash, + &cwindow->xwindow, cwindow); + + /* assume cwindow is at the top of the stack */ + screen->compositor_windows = g_list_prepend (screen->compositor_windows, + cwindow); + #endif /* HAVE_COMPOSITE_EXTENSIONS */ } @@ -193,11 +666,37 @@ meta_compositor_remove_window (MetaCompositor *compositor, Window xwindow) { #ifdef HAVE_COMPOSITE_EXTENSIONS + MetaCompositorWindow *cwindow; + MetaScreen *screen; + g_print ("compositor removing window 0x%lx\n", xwindow); if (!compositor->enabled) return; /* no extension */ + cwindow = g_hash_table_lookup (compositor->window_hash, + &xwindow); + + if (cwindow == NULL) + return; + + screen = meta_compositor_window_get_screen (cwindow); + + if (cwindow->last_painted_extents) + { + merge_and_destroy_damage_region (compositor, + screen, + cwindow->last_painted_extents); + cwindow->last_painted_extents = None; + } + + screen->compositor_windows = g_list_remove (screen->compositor_windows, + cwindow); + + /* Frees cwindow as side effect */ + g_hash_table_remove (compositor->window_hash, + &xwindow); + #endif /* HAVE_COMPOSITE_EXTENSIONS */ } @@ -206,9 +705,30 @@ meta_compositor_manage_screen (MetaCompositor *compositor, MetaScreen *screen) { #ifdef HAVE_COMPOSITE_EXTENSIONS + XRenderPictureAttributes pa; + if (!compositor->enabled) return; /* no extension */ + /* FIXME we need to handle root window resize by recreating the + * root_picture + */ + + g_assert (screen->root_picture == None); + + pa.subwindow_mode = IncludeInferiors; + + screen->root_picture = + XRenderCreatePicture (compositor->display->xdisplay, + screen->xroot, + XRenderFindVisualFormat (compositor->display->xdisplay, + DefaultVisual (compositor->display->xdisplay, + screen->number)), + CPSubwindowMode, + &pa); + + g_assert (screen->root_picture != None); + #endif /* HAVE_COMPOSITE_EXTENSIONS */ } @@ -216,10 +736,20 @@ void meta_compositor_unmanage_screen (MetaCompositor *compositor, MetaScreen *screen) { -#ifdef HAVE_COMPOSITE_EXTENSIONS +#ifdef HAVE_COMPOSITE_EXTENSIONS if (!compositor->enabled) return; /* no extension */ + XRenderFreePicture (screen->display->xdisplay, + screen->root_picture); + screen->root_picture = None; + + while (screen->compositor_windows != NULL) + { + MetaCompositorWindow *cwindow = screen->compositor_windows->data; + + meta_compositor_remove_window (compositor, cwindow->xwindow); + } #endif /* HAVE_COMPOSITE_EXTENSIONS */ } diff --git a/src/display.c b/src/display.c index aa36bfe6d..e807d04cd 100644 --- a/src/display.c +++ b/src/display.c @@ -1298,6 +1298,10 @@ event_callback (XEvent *event, } } #endif /* HAVE_SHAPE */ + + meta_compositor_process_event (display->compositor, + event, + window); switch (event->type) { @@ -1636,6 +1640,9 @@ event_callback (XEvent *event, case CreateNotify: break; case DestroyNotify: + meta_compositor_remove_window (display->compositor, + modified); + if (window) { if (display->grab_op != META_GRAB_OP_NONE && diff --git a/src/screen.c b/src/screen.c index d8d5defd4..35168158f 100644 --- a/src/screen.c +++ b/src/screen.c @@ -559,6 +559,9 @@ meta_screen_new (MetaDisplay *display, screen->showing_desktop = FALSE; + screen->compositor_windows = NULL; + screen->damage_region = None; + { XGCValues gc_values; diff --git a/src/screen.h b/src/screen.h index 24b29ab7e..93a2cb4f5 100644 --- a/src/screen.h +++ b/src/screen.h @@ -113,6 +113,11 @@ struct _MetaScreen /* gc for XOR on root window */ GC root_xor_gc; + + /* Managed by compositor.c; top of stack is first in list */ + GList *compositor_windows; + XID root_picture; + XID damage_region; }; MetaScreen* meta_screen_new (MetaDisplay *display,