diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/Makefile.am | 12 | ||||
-rw-r--r-- | src/compositor.c | 1590 | ||||
-rw-r--r-- | src/compositor.h | 37 | ||||
-rw-r--r-- | src/cwindow.c | 1639 | ||||
-rw-r--r-- | src/cwindow.h | 107 | ||||
-rw-r--r-- | src/lmcbits.c | 93 | ||||
-rw-r--r-- | src/lmcbits.h | 54 | ||||
-rw-r--r-- | src/lmctexture.c | 805 | ||||
-rw-r--r-- | src/lmctexture.h | 36 | ||||
-rw-r--r-- | src/lmctypes.h | 53 | ||||
-rw-r--r-- | src/matrix.c | 615 | ||||
-rw-r--r-- | src/matrix.h | 118 | ||||
-rw-r--r-- | src/snow.c | 380 | ||||
-rw-r--r-- | src/snow.h | 16 |
14 files changed, 4919 insertions, 636 deletions
diff --git a/src/Makefile.am b/src/Makefile.am index 3ed5e1db..9bdb8605 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -20,6 +20,8 @@ metacity_SOURCES= \ constraints.h \ core.c \ core.h \ + cwindow.c \ + cwindow.h \ delete.c \ display.c \ display.h \ @@ -49,8 +51,14 @@ metacity_SOURCES= \ inlinepixbufs.h \ keybindings.c \ keybindings.h \ + lmcbits.c \ + lmcbits.h \ + lmctexture.c \ + lmctexture.h \ main.c \ main.h \ + matrix.c \ + matrix.h \ menu.c \ menu.h \ metaaccellabel.c \ @@ -66,6 +74,8 @@ metacity_SOURCES= \ screen.h \ session.c \ session.h \ + snow.h \ + snow.c \ stack.c \ stack.h \ tabpopup.c \ @@ -129,7 +139,7 @@ bin_PROGRAMS=metacity metacity-theme-viewer libexec_PROGRAMS=metacity-dialog EFENCE= -metacity_LDADD=@METACITY_LIBS@ $(EFENCE) +metacity_LDADD=@METACITY_LIBS@ $(EFENCE) -lGL -lGLU metacity_theme_viewer_LDADD= @METACITY_LIBS@ libmetacity-private.la metacity_dialog_LDADD=@METACITY_LIBS@ diff --git a/src/compositor.c b/src/compositor.c index d82fadbf..c1b96863 100644 --- a/src/compositor.c +++ b/src/compositor.c @@ -26,64 +26,32 @@ #include "errors.h" #include "window.h" #include "frame.h" +#include "matrix.h" +#include <math.h> +#include "cwindow.h" +#include "snow.h" +#include <GL/gl.h> +#include <GL/glu.h> +#include <GL/glx.h> +#include <X11/extensions/shape.h> #ifdef HAVE_COMPOSITE_EXTENSIONS #include <X11/extensions/Xcomposite.h> #include <X11/extensions/Xdamage.h> #include <X11/extensions/Xrender.h> - -#if COMPOSITE_MAJOR > 0 || COMPOSITE_MINOR >= 2 -#define HAVE_NAME_WINDOW_PIXMAP 1 -#endif +#include "lmcbits.h" +#include "lmctexture.h" #endif /* HAVE_COMPOSITE_EXTENSIONS */ -#define SHADOW_OFFSET 3 #define FRAME_INTERVAL_MILLISECONDS ((int)(1000.0/40.0)) -/* Unlike MetaWindow, there's one of these for _all_ toplevel windows, - * override redirect or not. We also track unmapped windows as - * otherwise on window map we'd have to determine where the - * newly-mapped window was in the stack. A MetaCompositorWindow may - * correspond to a metacity window frame rather than an application - * window. - */ -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; - -#if HAVE_NAME_WINDOW_PIXMAP - Pixmap pixmap; -#endif - - unsigned int managed : 1; - unsigned int damaged : 1; - unsigned int viewable : 1; - - unsigned int screen_index : 8; - -#endif -} MetaCompositorWindow; - struct MetaCompositor { MetaDisplay *display; - + + World *world; + int composite_error_base; int composite_event_base; int damage_error_base; @@ -92,62 +60,119 @@ struct MetaCompositor int fixes_event_base; GHashTable *window_hash; - + guint repair_idle; - guint repair_timeout; guint enabled : 1; guint have_composite : 1; guint have_damage : 1; guint have_fixes : 1; -#ifdef HAVE_NAME_WINDOW_PIXMAP guint have_name_window_pixmap : 1; -#endif + guint debug_updates : 1; + + GList *ignored_damage; + + Window gl_window; }; #ifdef HAVE_COMPOSITE_EXTENSIONS static void -meta_compositor_window_free (MetaCompositorWindow *cwindow) +free_window_hash_value (void *v) { - g_assert (cwindow->damage != None); - - meta_error_trap_push (cwindow->compositor->display); - /* This seems to cause an error if the window - * is destroyed? - */ - XDamageDestroy (cwindow->compositor->display->xdisplay, - cwindow->damage); - -#if HAVE_NAME_WINDOW_PIXMAP - /* Free our window pixmap name */ - if (cwindow->pixmap != None) - XFreePixmap (cwindow->compositor->display->xdisplay, - cwindow->pixmap); -#endif - meta_error_trap_pop (cwindow->compositor->display, FALSE); - - g_free (cwindow); + CWindow *cwindow = v; + + g_print ("freeing cwindow %lx\n", cwindow_get_xwindow (cwindow)); + cwindow_free (cwindow); } #endif /* HAVE_COMPOSITE_EXTENSIONS */ -#ifdef HAVE_COMPOSITE_EXTENSIONS static void -free_window_hash_value (void *v) +print_region (Display *dpy, const char *name, XserverRegion region) { - MetaCompositorWindow *cwindow = v; + XRectangle *rects; + int i, n_rects; + + rects = XFixesFetchRegion (dpy, region, &n_rects); + + g_print ("region \"%s\":\n", name); + for (i = 0; i < n_rects; ++i) + g_print (" %d %d %d %d\n", rects[i].x, rects[i].y, rects[i].width, rects[i].height); + XFree (rects); +} - meta_compositor_window_free (cwindow); +static gboolean +update_world (gpointer data) +{ + static double time; + + MetaCompositor *compositor = data; + World *world = compositor->world; + MetaScreen *screen = world_get_screen (world); + XserverRegion region; + + region = world_invalidate (world); + screen = world_get_screen (world); + + meta_compositor_invalidate_region (compositor, screen, region); + XFixesDestroyRegion (compositor->display->xdisplay, region); + + world_set_time (world, time); /* FIXME */ + + region = world_invalidate (world); + meta_compositor_invalidate_region (compositor, screen, region); + XFixesDestroyRegion (compositor->display->xdisplay, region); + + time += 0.001; + + return TRUE; +} + +static XVisualInfo * +init_gl (Display *xdisplay, + int screen, + GLXContext *ctx) +{ + int attrib[] = { GLX_RGBA, + GLX_RED_SIZE, 1, + GLX_GREEN_SIZE, 1, + GLX_BLUE_SIZE, 1, + GLX_STENCIL_SIZE, 1, + GLX_DOUBLEBUFFER, + GLX_DEPTH_SIZE, 1, + None }; + XVisualInfo *visinfo; + GLXContext context; + + visinfo = glXChooseVisual( xdisplay, screen, attrib ); + if (!visinfo) + { + g_printerr ("Error: couldn't get an RGB, Double-buffered visual.\n"); + return NULL; + } + + context = glXCreateContext( xdisplay, visinfo, NULL, True ); + + if (!context) + { + g_printerr ("glXCreateContext failed.\n"); + return NULL; + } + + *ctx = context; + + g_print ("visual depth: %d\n", visinfo->depth); + + return visinfo; } -#endif /* HAVE_COMPOSITE_EXTENSIONS */ MetaCompositor* meta_compositor_new (MetaDisplay *display) { #ifdef HAVE_COMPOSITE_EXTENSIONS MetaCompositor *compositor; - + compositor = g_new0 (MetaCompositor, 1); - + compositor->display = display; if (!XCompositeQueryExtension (display->xdisplay, @@ -162,22 +187,16 @@ meta_compositor_new (MetaDisplay *display) int composite_major, composite_minor; compositor->have_composite = TRUE; - -#if HAVE_NAME_WINDOW_PIXMAP + XCompositeQueryVersion (display->xdisplay, &composite_major, &composite_minor); - -#if 0 - if (composite_major > 0 || composite_minor >= 2) - compositor->have_name_window_pixmap = TRUE; -#endif -#endif + } - + meta_topic (META_DEBUG_COMPOSITOR, "Composite extension event base %d error base %d\n", compositor->composite_event_base, compositor->composite_error_base); - + if (!XDamageQueryExtension (display->xdisplay, &compositor->damage_event_base, &compositor->damage_error_base)) @@ -187,7 +206,7 @@ meta_compositor_new (MetaDisplay *display) } else compositor->have_damage = TRUE; - + meta_topic (META_DEBUG_COMPOSITOR, "Damage extension event base %d error base %d\n", compositor->damage_event_base, compositor->damage_error_base); @@ -201,7 +220,7 @@ meta_compositor_new (MetaDisplay *display) } else compositor->have_fixes = TRUE; - + meta_topic (META_DEBUG_COMPOSITOR, "Fixes extension event base %d error base %d\n", compositor->fixes_event_base, compositor->fixes_error_base); @@ -212,6 +231,15 @@ meta_compositor_new (MetaDisplay *display) META_DISPLAY_HAS_RENDER (compositor->display))) { meta_topic (META_DEBUG_COMPOSITOR, "Failed to find all extensions needed for compositing manager, disabling compositing manager\n"); + + if (!compositor->have_composite) + g_print ("no composite\n"); + if (!compositor->have_fixes) + g_print ("no fixes\n"); + if (!compositor->have_damage) + g_print ("have damage\n"); + + g_assert (!compositor->enabled); return compositor; } @@ -229,24 +257,24 @@ meta_compositor_new (MetaDisplay *display) #endif /* HAVE_COMPOSITE_EXTENSIONS */ } +void +meta_compositor_set_debug_updates (MetaCompositor *compositor, + gboolean debug_updates) +{ + compositor->debug_updates = !!debug_updates; +} + #ifdef HAVE_COMPOSITE_EXTENSIONS static void remove_repair_idle (MetaCompositor *compositor) { - if (compositor->repair_idle || compositor->repair_timeout) - meta_topic (META_DEBUG_COMPOSITOR, "Damage idle removed\n"); - - if (compositor->repair_idle != 0) + if (compositor->repair_idle) { + meta_topic (META_DEBUG_COMPOSITOR, "Damage idle removed\n"); + g_source_remove (compositor->repair_idle); compositor->repair_idle = 0; } - - if (compositor->repair_timeout != 0) - { - g_source_remove (compositor->repair_timeout); - compositor->repair_timeout = 0; - } } #endif /* HAVE_COMPOSITE_EXTENSIONS */ @@ -258,7 +286,7 @@ meta_compositor_unref (MetaCompositor *compositor) * there's no ref() */ remove_repair_idle (compositor); - + if (compositor->window_hash) g_hash_table_destroy (compositor->window_hash); @@ -266,70 +294,121 @@ meta_compositor_unref (MetaCompositor *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; - - r.width += SHADOW_OFFSET; - r.height += SHADOW_OFFSET; - - return XFixesCreateRegion (cwindow->compositor->display->xdisplay, &r, 1); -} -#endif /* HAVE_COMPOSITE_EXTENSIONS */ +double tmp; +static double last; +static GTimer *timer ; #ifdef HAVE_COMPOSITE_EXTENSIONS static void -window_get_paint_bounds (MetaCompositorWindow *cwindow, - int *x, - int *y, - int *w, - int *h) +draw_windows (MetaCompositor *compositor, + MetaScreen *screen, + GList *list, + XserverRegion damaged_region, + Picture picture) { -#ifdef HAVE_NAME_WINDOW_PIXMAP - if (cwindow->pixmap != None) + CWindow *cwindow; + XserverRegion region_below; + Display *dpy = compositor->display->xdisplay; + + if (!list) + return; + + cwindow = list->data; + + region_below = XFixesCreateRegion (dpy, NULL, 0); + XFixesCopyRegion (dpy, region_below, damaged_region); + + if (!cwindow_is_translucent (cwindow) && + !cwindow_get_input_only (cwindow) && cwindow_get_viewable (cwindow)) { - *x = cwindow->x; - *y = cwindow->y; - *w = cwindow->width + cwindow->border_width * 2; - *h = cwindow->height + cwindow->border_width * 2; + XserverRegion opaque = cwindow_get_opaque_region (cwindow); + + XFixesSubtractRegion (dpy, region_below, region_below, opaque); + + XFixesDestroyRegion (dpy, opaque); } - else + + draw_windows (compositor, screen, list->next, region_below, picture); + + XFixesDestroyRegion (dpy, region_below); + + if (cwindow_get_viewable (cwindow) && cwindow_get_xwindow (cwindow) != compositor->gl_window) + { + cwindow_draw (cwindow, picture, damaged_region); +#if 0 + g_print ("drawing a window: %f\n", (tmp = g_timer_elapsed (timer, NULL)) - last); + last = tmp; #endif - { - *x = cwindow->x + cwindow->border_width; - *y = cwindow->y + cwindow->border_width; - *w = cwindow->width; - *h = cwindow->height; - } + } } -static void -paint_screen (MetaCompositor *compositor, - MetaScreen *screen, - XserverRegion damage_region) +static Picture +create_root_buffer (MetaScreen *screen, Pixmap *pixmap) { - XserverRegion region; - Picture buffer_picture; + Display *display = screen->display->xdisplay; Pixmap buffer_pixmap; - Display *xdisplay; + XGCValues value; XRenderPictFormat *format; - GList *tmp; - GC gc; - - meta_topic (META_DEBUG_COMPOSITOR, "Repainting screen %d root 0x%lx\n", - screen->number, screen->xroot); + Picture buffer_picture; + + buffer_pixmap = XCreatePixmap (display, screen->xroot, + screen->width, + screen->height, + DefaultDepth (display, + screen->number)); + + value.function = GXcopy; + value.subwindow_mode = IncludeInferiors; + + format = XRenderFindVisualFormat (display, + DefaultVisual (display, + screen->number)); + + buffer_picture = XRenderCreatePicture (display, + buffer_pixmap, + format, + 0, 0); + + if (pixmap) + *pixmap = buffer_pixmap; + else + XFreePixmap (display, buffer_pixmap); + + return buffer_picture; +} - /* meta_display_grab (screen->display); */ +static void +paint_buffer (MetaScreen *screen, + Pixmap pixmap, + XserverRegion damage_region) +{ + XGCValues value; + GC gc; + value.function = GXcopy; + value.subwindow_mode = IncludeInferiors; - xdisplay = screen->display->xdisplay; + gc = XCreateGC (screen->display->xdisplay, pixmap, GCFunction | GCSubwindowMode, &value); + if (damage_region) + XFixesSetGCClipRegion (screen->display->xdisplay, gc, 0, 0, damage_region); + + XCopyArea (screen->display->xdisplay, + pixmap, screen->xroot, gc, + 0, 0, screen->width, screen->height, 0, 0); + + XFreeGC (screen->display->xdisplay, gc); +} + +static void +ensure_repair_idle (MetaCompositor *compositor); +static XserverRegion +do_paint_screen (MetaCompositor *compositor, + MetaScreen *screen, + Picture picture, + XserverRegion damage_region) +{ + Display *xdisplay = compositor->display->xdisplay; + XserverRegion region; if (damage_region == None) { XRectangle r; @@ -346,142 +425,224 @@ paint_screen (MetaCompositor *compositor, region = XFixesCreateRegion (xdisplay, NULL, 0); XFixesCopyRegion (compositor->display->xdisplay, - region, - damage_region); + region, + damage_region); } - buffer_pixmap = XCreatePixmap (xdisplay, screen->xroot, - screen->width, - screen->height, - DefaultDepth (xdisplay, - screen->number)); - - gc = XCreateGC (xdisplay, buffer_pixmap, 0, NULL); - XSetForeground (xdisplay, gc, WhitePixel (xdisplay, screen->number)); - XFixesSetGCClipRegion (xdisplay, gc, 0, 0, region); - XFillRectangle (xdisplay, buffer_pixmap, gc, 0, 0, - screen->width, screen->height); +#if 0 + { + XGCValues values; + values.subwindow_mode = IncludeInferiors; + values.foreground = 0x00ff00ff; /* shocking pink */ + GC gc = XCreateGC (screen->display->xdisplay, + screen->xroot, + GCSubwindowMode | GCForeground, &values); + + XFixesSetGCClipRegion (screen->display->xdisplay, gc, + 0, 0, region); + + XFillRectangle (screen->display->xdisplay, screen->xroot, gc, + 0, 0, screen->width, screen->height); + XSync (screen->display->xdisplay, False); + } +#endif - format = XRenderFindVisualFormat (xdisplay, - DefaultVisual (xdisplay, - screen->number)); - buffer_picture = XRenderCreatePicture (xdisplay, - buffer_pixmap, - format, - 0, 0); + if (USE_RENDER) + { + meta_error_trap_push (compositor->display); + XFixesSetPictureClipRegion (xdisplay, + picture, 0, 0, + region); + } + else + { + GList *list; - /* set clip */ - XFixesSetPictureClipRegion (xdisplay, - buffer_picture, 0, 0, - region); + glShadeModel (GL_FLAT); + glDisable (GL_TEXTURE_2D); + glDisable (GL_BLEND); - /* 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; - XRenderColor shadow_color; - MetaWindow *window; + glViewport (0, 0, screen->width, screen->height); - shadow_color.red = 0; - shadow_color.green = 0; - shadow_color.blue = 0; - shadow_color.alpha = 0x90c0; +#if 0 + glClearColor (0.0, 0.5, 0.5, 0.0); + glClear (GL_COLOR_BUFFER_BIT); + + glLoadIdentity (); + + glColor4f (0.8, 0.3, 0.8, 0.8); + + glBegin (GL_TRIANGLES); + + glVertex2f (0.0, 0.0); + glVertex2f (10, 100); + glVertex2f (20, 0.0); + + glEnd (); + + glColor4f (0.2, 0.2, 1.0, 0.5); + + glBegin (GL_TRIANGLES); + + glVertex2f (-1.0, 0.0); + glVertex2f (120, 100); + glVertex2f (200, 23.0); + + glEnd(); +#endif - if (cwindow->picture == None) /* InputOnly */ - goto next; +#if 0 + glColor4f (g_random_double (), + g_random_double (), + g_random_double (), + 1.0); +#endif - if (!cwindow->viewable) - goto next; /* not viewable */ +#if 0 + glBegin (GL_QUADS); + + glVertex2f (-1.0, -1.0); + glVertex2f (1.0, -1.0); + glVertex2f (1.0, 1.0); + glVertex2f (-1.0, 1.0); - if (cwindow->last_painted_extents) - XFixesDestroyRegion (xdisplay, - cwindow->last_painted_extents); + glEnd (); +#endif - cwindow->last_painted_extents = window_extents (cwindow); + glLoadIdentity(); - /* XFixesSubtractRegion (dpy, region, region, 0, 0, w->borderSize, 0, 0); */ + gluOrtho2D (0, screen->width, screen->height, 0); + glDisable (GL_SCISSOR_TEST); + } + + if (!timer) + timer = g_timer_new (); - meta_topic (META_DEBUG_COMPOSITOR, " Compositing window 0x%lx %d,%d %dx%d\n", - cwindow->xwindow, - cwindow->x, cwindow->y, - cwindow->width, cwindow->height); +#if 0 + g_print ("outside: %f\n", (tmp = g_timer_elapsed (timer, NULL)) - last); + last = tmp; +#endif - window = meta_display_lookup_x_window (compositor->display, - cwindow->xwindow); - if (window != NULL && - window == compositor->display->grab_window && - (meta_grab_op_is_resizing (compositor->display->grab_op) || - meta_grab_op_is_moving (compositor->display->grab_op))) - { - /* Draw window transparent while resizing */ - int x, y, w, h; - - window_get_paint_bounds (cwindow, &x, &y, &w, &h); - - XRenderComposite (xdisplay, - PictOpOver, /* PictOpOver for alpha, PictOpSrc without */ - cwindow->picture, - screen->trans_picture, - buffer_picture, - 0, 0, 0, 0, - x, y, w, h); - } - else - { - /* Draw window normally */ - int x, y, w, h; - - window_get_paint_bounds (cwindow, &x, &y, &w, &h); - - /* superlame drop shadow */ - XRenderFillRectangle (xdisplay, PictOpOver, - buffer_picture, - &shadow_color, - cwindow->x + SHADOW_OFFSET, - cwindow->y + SHADOW_OFFSET, - cwindow->width, cwindow->height); - - XRenderComposite (xdisplay, - PictOpSrc, /* PictOpOver for alpha, PictOpSrc without */ - cwindow->picture, - None, - buffer_picture, - 0, 0, 0, 0, - x, y, w, h); - } + draw_windows (compositor, screen, screen->compositor_windows, region, picture); + if (USE_RENDER) + { + meta_error_trap_pop (compositor->display, FALSE); + } + else + { + glShadeModel (GL_FLAT); + glDisable (GL_TEXTURE_2D); + glDisable (GL_BLEND); + + /* FIXME: we should probably grab the server around the raise/swap */ + XRaiseWindow (compositor->display->xdisplay, compositor->gl_window); - next: - tmp = tmp->prev; - } - meta_error_trap_pop (compositor->display, FALSE); +#if 0 + g_print ("painting window: %f\n", (tmp = g_timer_elapsed (timer, NULL)) - last); + last = tmp; +#endif + + glXSwapBuffers (screen->display->xdisplay, compositor->gl_window); - /* Copy buffer to root window */ - meta_topic (META_DEBUG_COMPOSITOR, "Copying buffer to root window 0x%lx picture 0x%lx\n", - screen->xroot, screen->root_picture); +#if 0 + g_print ("in swap: %f\n", (tmp = g_timer_elapsed (timer, NULL)) - last); +#endif + last = tmp; + +#if 0 + glFinish(); +#endif -#if 1 - XFixesSetPictureClipRegion (xdisplay, - screen->root_picture, - 0, 0, region); +#if 0 + g_print ("inside: %f\n", (tmp = g_timer_elapsed (timer, NULL)) - last); + last = tmp; +#endif + +#if 0 + XGCValues values; + values.subwindow_mode = IncludeInferiors; + Window root = RootWindow (screen->display->xdisplay, 0); + GC gc = XCreateGC (screen->display->xdisplay, root, GCSubwindowMode, &values); + + XCopyArea (screen->display->xdisplay, + compositor->gl_window, root, gc, + 0, 0, 1600, 1200, 0, 0); #endif + } - /* 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); - XFreeGC (xdisplay, gc); - - /* meta_display_ungrab (screen->display); */ + return region; +} +static void +paint_screen (MetaCompositor *compositor, + MetaScreen *screen, + XserverRegion damage_region) +{ + Picture buffer_picture; + Display *xdisplay; + XserverRegion region; + Pixmap pixmap; + + meta_topic (META_DEBUG_COMPOSITOR, "Repainting screen %d root 0x%lx\n", + screen->number, screen->xroot); + + /* meta_display_grab (screen->display); */ + + xdisplay = screen->display->xdisplay; + + if (USE_RENDER) + buffer_picture = create_root_buffer (screen, &pixmap); + else + buffer_picture = None; + + /* set clip */ + region = do_paint_screen (compositor, screen, buffer_picture, damage_region); + + /* Copy buffer to root window */ + meta_topic (META_DEBUG_COMPOSITOR, "Copying buffer to root window 0x%lx picture 0x%lx\n", + screen->xroot, screen->root_picture); + + if (USE_RENDER) + { + + XFixesSetPictureClipRegion (xdisplay, + screen->root_picture, + 0, 0, region); + + XFixesSetPictureClipRegion (xdisplay, buffer_picture, 0, 0, None); + + if (compositor->debug_updates) + { +#define ALPHA 0.5 + XRenderColor hilight = { ALPHA * 0xFFFF, ALPHA * 0xFFFF, ALPHA * 0x0000, ALPHA * 0xFFFF }; + XRenderFillRectangle (screen->display->xdisplay, PictOpOver, + screen->root_picture, &hilight, + 0, 0, screen->width, screen->height); + XSync (xdisplay, False); + g_usleep (20000); + } + +#if 0 + world_paint (compositor->world, buffer_picture); +#endif + + paint_buffer (screen, pixmap, damage_region); + + XFreePixmap (screen->display->xdisplay, pixmap); + } + + XFixesDestroyRegion (xdisplay, region); + + if (USE_RENDER) + { + /* XFixesSetPictureClipRegion (xdisplay, buffer_picture, 0, 0, None); */ + XRenderFreePicture (xdisplay, buffer_picture); + + /* meta_display_ungrab (screen->display); */ + } + XSync (screen->display->xdisplay, False); } #endif /* HAVE_COMPOSITE_EXTENSIONS */ @@ -496,9 +657,14 @@ do_repair (MetaCompositor *compositor) while (tmp != NULL) { MetaScreen *s = tmp->data; - + if (s->damage_region != None) { +#if 0 + print_region (compositor->display->xdisplay, + "damage region", + s->damage_region); +#endif paint_screen (compositor, s, s->damage_region); XFixesDestroyRegion (s->display->xdisplay, @@ -508,17 +674,15 @@ do_repair (MetaCompositor *compositor) tmp = tmp->next; } - + remove_repair_idle (compositor); } #endif /* HAVE_COMPOSITE_EXTENSIONS */ #ifdef HAVE_COMPOSITE_EXTENSIONS -static gboolean -repair_idle_func (void *data) +gboolean +meta_compositor_repair_now (MetaCompositor *compositor) { - MetaCompositor *compositor = data; - compositor->repair_idle = 0; do_repair (compositor); @@ -528,101 +692,66 @@ repair_idle_func (void *data) #ifdef HAVE_COMPOSITE_EXTENSIONS -static gboolean -repair_timeout_func (void *data) -{ - MetaCompositor *compositor = data; - - compositor->repair_timeout = 0; - do_repair (compositor); - - 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_full (META_PRIORITY_COMPOSITE, - repair_idle_func, compositor, NULL); - compositor->repair_timeout = g_timeout_add (FRAME_INTERVAL_MILLISECONDS, - repair_timeout_func, compositor); - - meta_topic (META_DEBUG_COMPOSITOR, "Damage idle queued\n"); -} -#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) - { - XFixesUnionRegion (compositor->display->xdisplay, - screen->damage_region, - region, screen->damage_region); - XFixesDestroyRegion (compositor->display->xdisplay, - region); - } - else + + if (!compositor->world) { - screen->damage_region = region; + compositor->world = world_new (compositor->display->xdisplay, + meta_display_screen_for_x_screen ( + compositor->display, + ScreenOfDisplay (compositor->display->xdisplay, + DefaultScreen (compositor->display->xdisplay)))); + + g_print ("screen %p\n", world_get_screen (compositor->world)); + +#if 0 + g_timeout_add_full (G_PRIORITY_HIGH, 50, update_world, compositor, NULL); +#endif } - - ensure_repair_idle (compositor); + + compositor->repair_idle = g_idle_add_full (META_PRIORITY_COMPOSITE, + meta_compositor_repair_now, compositor, NULL); + + meta_topic (META_DEBUG_COMPOSITOR, "Damage idle queued\n"); } #endif /* HAVE_COMPOSITE_EXTENSIONS */ #ifdef HAVE_COMPOSITE_EXTENSIONS -static void -merge_damage_region (MetaCompositor *compositor, - MetaScreen *screen, - XserverRegion region) +void +meta_compositor_invalidate_region (MetaCompositor *compositor, + MetaScreen *screen, + XserverRegion invalid_area) { if (screen->damage_region == None) screen->damage_region = XFixesCreateRegion (compositor->display->xdisplay, NULL, 0); - - meta_topic (META_DEBUG_COMPOSITOR, - "(Merging damage region)\n"); + +#if 0 + if (compositor->debug_updates) + { +# define ALPHA 0.1 + XRenderColor hilight = { ALPHA * 0x0000, ALPHA * 0x1000, ALPHA * 0xFFFF, ALPHA * 0xFFFF }; + XFixesSetPictureClipRegion (screen->display->xdisplay, screen->root_picture, 0, 0, invalid_area); + XRenderFillRectangle (screen->display->xdisplay, PictOpOver, + screen->root_picture, &hilight, + 0, 0, screen->width, screen->height); + XSync (screen->display->xdisplay, False); + } +#endif + +#if 0 + print_region (compositor->display->xdisplay, "invalidate", invalid_area); + meta_print_top_of_stack (4); +#endif XFixesUnionRegion (compositor->display->xdisplay, screen->damage_region, - region, screen->damage_region); - + invalid_area, screen->damage_region); + ensure_repair_idle (compositor); } #endif /* HAVE_COMPOSITE_EXTENSIONS */ @@ -632,135 +761,96 @@ static void process_damage_notify (MetaCompositor *compositor, XDamageNotifyEvent *event) { - MetaCompositorWindow *cwindow; - XserverRegion region; - MetaScreen *screen; + CWindow *cwindow; + +#if 0 + g_print ("damage on %lx\n", event->drawable); +#endif cwindow = g_hash_table_lookup (compositor->window_hash, &event->drawable); + if (cwindow == NULL) - return; + { + g_print ("spurious damage\n"); + return; + } - region = XFixesCreateRegion (compositor->display->xdisplay, NULL, 0); + cwindow_process_damage_notify (cwindow, event); +} +#endif /* HAVE_COMPOSITE_EXTENSIONS */ - /* translate region to screen; can error if window of damage is - * destroyed - */ - meta_error_trap_push (compositor->display); - XDamageSubtract (compositor->display->xdisplay, - cwindow->damage, None, region); - meta_error_trap_pop (compositor->display, FALSE); +static void +handle_restacking (MetaCompositor *compositor, + CWindow *cwindow, + CWindow *above) +{ + GList *window_link, *above_link; + MetaScreen *screen; - XFixesTranslateRegion (compositor->display->xdisplay, - region, - cwindow->x, - cwindow->y); + if (!cwindow) + return; - screen = meta_compositor_window_get_screen (cwindow); + screen = cwindow_get_screen (cwindow); - merge_and_destroy_damage_region (compositor, screen, region); + window_link = g_list_find (screen->compositor_windows, cwindow); + above_link = g_list_find (screen->compositor_windows, above); + + if (window_link->next != above_link) + { + screen->compositor_windows = g_list_delete_link (screen->compositor_windows, window_link); + screen->compositor_windows = g_list_insert_before (screen->compositor_windows, above_link, cwindow); + } +} + +static void +raise_gl_window (MetaCompositor *compositor) +{ + CWindow *cwindow = g_hash_table_lookup (compositor->window_hash, &compositor->gl_window); + if (cwindow) + { + MetaScreen *screen = cwindow_get_screen (cwindow); + GList *gl_link = g_list_find (screen->compositor_windows, cwindow); + + if (gl_link) + { + XRaiseWindow (compositor->display->xdisplay, compositor->gl_window); + screen->compositor_windows = g_list_delete_link (screen->compositor_windows, gl_link); + screen->compositor_windows = g_list_prepend (screen->compositor_windows, cwindow); + } + } } -#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 *cwindow; cwindow = g_hash_table_lookup (compositor->window_hash, &event->window); +#if 0 + g_print ("configure: %lx above %lx\n", event->window, event->above); +#endif + if (cwindow == NULL) - return; - - screen = meta_compositor_window_get_screen (cwindow); - - g_assert (screen != NULL); + { + g_print ("no window for event->window\n"); + return; + } - if (screen->xroot != event->event) - { - meta_topic (META_DEBUG_COMPOSITOR, - "ConfigureNotify for 0x%lx received by 0x%lx not using\n", - event->window, event->event); - return; /* ignore ConfigureNotify not received on root window */ - } +#if 0 + g_print ("gl window: %lx\n", compositor->gl_window); +#endif - meta_topic (META_DEBUG_COMPOSITOR, - "ConfigureNotify for 0x%lx received by 0x%lx %d,%d %dx%d\n", - event->window, event->event, - event->x, event->y, event->width, event->height); - - if (cwindow->last_painted_extents) - { - merge_and_destroy_damage_region (compositor, - screen, - cwindow->last_painted_extents); - cwindow->last_painted_extents = None; - } + handle_restacking (compositor, cwindow, + g_hash_table_lookup (compositor->window_hash, &event->above)); - 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); + cwindow_process_configure_notify (cwindow, event); - if (link->next) - above = ((MetaCompositorWindow*) link->next)->xwindow; - else - above = None; + raise_gl_window (compositor); - 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 */ @@ -773,21 +863,26 @@ process_expose (MetaCompositor *compositor, XserverRegion region; MetaScreen *screen; XRectangle r; - + screen = meta_display_screen_for_root (compositor->display, event->window); - + if (screen == NULL || screen->root_picture == None) return; - + r.x = 0; r.y = 0; r.width = screen->width; r.height = screen->height; region = XFixesCreateRegion (compositor->display->xdisplay, &r, 1); - merge_and_destroy_damage_region (compositor, screen, region); + meta_compositor_invalidate_region (compositor, + screen, + region); + + XFixesDestroyRegion (compositor->display->xdisplay, region); } + #endif /* HAVE_COMPOSITE_EXTENSIONS */ #ifdef HAVE_COMPOSITE_EXTENSIONS @@ -795,13 +890,13 @@ static void process_map (MetaCompositor *compositor, XMapEvent *event) { - MetaCompositorWindow *cwindow; + CWindow *cwindow; MetaScreen *screen; - + /* See if window was mapped as child of root */ screen = meta_display_screen_for_root (compositor->display, event->event); - + if (screen == NULL || screen->root_picture == None) { meta_topic (META_DEBUG_COMPOSITOR, @@ -827,17 +922,21 @@ process_map (MetaCompositor *compositor, event->window); } else - { - meta_topic (META_DEBUG_COMPOSITOR, - "Map window 0x%lx\n", event->window); - meta_compositor_add_window (compositor, - event->window, &attrs); + { + g_print ("Map window 0x%lx\n", event->window); + meta_compositor_add_window (compositor, + event->window, &attrs); } } else { - cwindow->viewable = TRUE; + cwindow_set_viewable (cwindow, TRUE); } + + /* We don't actually need to invalidate anything, because we will + * get damage events as the server fills the background and the client + * draws the window + */ } #endif /* HAVE_COMPOSITE_EXTENSIONS */ @@ -846,13 +945,13 @@ static void process_unmap (MetaCompositor *compositor, XUnmapEvent *event) { - MetaCompositorWindow *cwindow; + CWindow *cwindow; MetaScreen *screen; - + /* See if window was unmapped as child of root */ screen = meta_display_screen_for_root (compositor->display, event->event); - + if (screen == NULL || screen->root_picture == None) { meta_topic (META_DEBUG_COMPOSITOR, @@ -865,14 +964,14 @@ process_unmap (MetaCompositor *compositor, &event->window); if (cwindow != NULL) { - cwindow->viewable = FALSE; - - if (cwindow->last_painted_extents) + cwindow_set_viewable (cwindow, FALSE); + + if (cwindow_get_last_painted_extents (cwindow)) { - merge_and_destroy_damage_region (compositor, - screen, - cwindow->last_painted_extents); - cwindow->last_painted_extents = None; + meta_compositor_invalidate_region (compositor, + screen, + cwindow_get_last_painted_extents (cwindow)); + cwindow_set_last_painted_extents (cwindow, None); } } } @@ -885,10 +984,10 @@ process_create (MetaCompositor *compositor, { MetaScreen *screen; XWindowAttributes attrs; - + screen = meta_display_screen_for_root (compositor->display, event->parent); - + if (screen == NULL || screen->root_picture == None) { meta_topic (META_DEBUG_COMPOSITOR, @@ -896,7 +995,7 @@ process_create (MetaCompositor *compositor, event->parent, event->window); return; } - + meta_error_trap_push_with_return (compositor->display); XGetWindowAttributes (compositor->display->xdisplay, @@ -911,6 +1010,7 @@ process_create (MetaCompositor *compositor, { meta_topic (META_DEBUG_COMPOSITOR, "Create window 0x%lx, adding\n", event->window); + meta_compositor_add_window (compositor, event->window, &attrs); } @@ -923,10 +1023,10 @@ process_destroy (MetaCompositor *compositor, XDestroyWindowEvent *event) { MetaScreen *screen; - + screen = meta_display_screen_for_root (compositor->display, event->event); - + if (screen == NULL || screen->root_picture == None) { meta_topic (META_DEBUG_COMPOSITOR, @@ -934,7 +1034,7 @@ process_destroy (MetaCompositor *compositor, event->event, event->window); return; } - + meta_topic (META_DEBUG_COMPOSITOR, "Destroy window 0x%lx\n", event->window); meta_compositor_remove_window (compositor, event->window); @@ -952,12 +1052,14 @@ process_reparent (MetaCompositor *compositor, */ MetaScreen *event_screen; MetaScreen *parent_screen; - MetaCompositorWindow *cwindow; + CWindow *cwindow; XWindowAttributes attrs; event_screen = meta_display_screen_for_root (compositor->display, event->event); + g_print ("reparent\n"); + if (event_screen == NULL || event_screen->root_picture == None) { meta_topic (META_DEBUG_COMPOSITOR, @@ -965,11 +1067,11 @@ process_reparent (MetaCompositor *compositor, event->event, event->window); return; } - + meta_topic (META_DEBUG_COMPOSITOR, "Reparent window 0x%lx new parent 0x%lx received on 0x%lx\n", event->window, event->parent, event->event); - + parent_screen = meta_display_screen_for_root (compositor->display, event->parent); @@ -989,11 +1091,11 @@ process_reparent (MetaCompositor *compositor, meta_topic (META_DEBUG_COMPOSITOR, "Window reparented to new screen at %d,%d\n", event->x, event->y); - cwindow->x = event->x; - cwindow->y = event->y; + cwindow_set_x (cwindow, event->x); + cwindow_set_y (cwindow, event->y); return; } - + meta_error_trap_push_with_return (compositor->display); XGetWindowAttributes (compositor->display->xdisplay, @@ -1001,6 +1103,7 @@ process_reparent (MetaCompositor *compositor, if (meta_error_trap_pop_with_return (compositor->display, TRUE) != Success) { + g_print ("attrs\n"); meta_topic (META_DEBUG_COMPOSITOR, "Failed to get attributes for window 0x%lx\n", event->window); } @@ -1009,6 +1112,7 @@ process_reparent (MetaCompositor *compositor, meta_topic (META_DEBUG_COMPOSITOR, "Reparent window 0x%lx into screen 0x%lx, adding\n", event->window, event->parent); + g_print ("adding\n"); meta_compositor_add_window (compositor, event->window, &attrs); } @@ -1023,7 +1127,7 @@ meta_compositor_process_event (MetaCompositor *compositor, #ifdef HAVE_COMPOSITE_EXTENSIONS if (!compositor->enabled) return; /* no extension */ - + /* FIXME support CirculateNotify */ if (event->type == (compositor->damage_event_base + XDamageNotify)) @@ -1079,106 +1183,58 @@ meta_compositor_add_window (MetaCompositor *compositor, XWindowAttributes *attrs) { #ifdef HAVE_COMPOSITE_EXTENSIONS - MetaCompositorWindow *cwindow; + CWindow *cwindow; MetaScreen *screen; - Damage damage; - XRenderPictFormat *format; - XRenderPictureAttributes pa; + XserverRegion region; if (!compositor->enabled) return; /* no extension */ +#if 0 + if (xwindow == compositor->gl_window) + return; +#endif + screen = meta_screen_for_x_screen (attrs->screen); g_assert (screen != NULL); cwindow = g_hash_table_lookup (compositor->window_hash, &xwindow); - + if (cwindow != NULL) { meta_topic (META_DEBUG_COMPOSITOR, "Window 0x%lx already added\n", xwindow); return; } - + meta_topic (META_DEBUG_COMPOSITOR, "Adding window 0x%lx (%s) to compositor\n", xwindow, attrs->map_state == IsViewable ? "mapped" : "unmapped"); - /* 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; - -#if HAVE_NAME_WINDOW_PIXMAP - if (compositor->have_name_window_pixmap) - { - meta_error_trap_push (compositor->display); - cwindow->pixmap = XCompositeNameWindowPixmap (compositor->display->xdisplay, - cwindow->xwindow); - meta_error_trap_pop (compositor->display, FALSE); - } -#endif + cwindow = cwindow_new (compositor, xwindow, attrs); - /* viewable == mapped for the root window, since root can't be unmapped */ - cwindow->viewable = (attrs->map_state == IsViewable); /* FIXME this assertion can fail somehow... */ g_assert (attrs->map_state != IsUnviewable); - pa.subwindow_mode = IncludeInferiors; - - if (attrs->class != InputOnly) - { - format = XRenderFindVisualFormat (compositor->display->xdisplay, - attrs->visual); - cwindow->picture = XRenderCreatePicture (compositor->display->xdisplay, -#if HAVE_NAME_WINDOW_PIXMAP - cwindow->pixmap != None ? - cwindow->pixmap : - cwindow->xwindow, -#else - cwindow->xwindow, -#endif - format, - CPSubwindowMode, - &pa); - } - else - { - cwindow->picture = None; - } - g_hash_table_insert (compositor->window_hash, - &cwindow->xwindow, cwindow); - + cwindow_get_xid_address (cwindow), cwindow); + /* assume cwindow is at the top of the stack as it was either just * created or just reparented to the root window */ screen->compositor_windows = g_list_prepend (screen->compositor_windows, cwindow); - + /* schedule paint of the new window */ - region = window_extents (cwindow); - merge_and_destroy_damage_region (compositor, screen, region); + region = cwindow_extents (cwindow); + + meta_compositor_invalidate_region (compositor, screen, region); + + XFixesDestroyRegion (compositor->display->xdisplay, region); #endif /* HAVE_COMPOSITE_EXTENSIONS */ } @@ -1187,7 +1243,7 @@ meta_compositor_remove_window (MetaCompositor *compositor, Window xwindow) { #ifdef HAVE_COMPOSITE_EXTENSIONS - MetaCompositorWindow *cwindow; + CWindow *cwindow; MetaScreen *screen; if (!compositor->enabled) @@ -1195,27 +1251,27 @@ meta_compositor_remove_window (MetaCompositor *compositor, cwindow = g_hash_table_lookup (compositor->window_hash, &xwindow); - + if (cwindow == NULL) { meta_topic (META_DEBUG_COMPOSITOR, "Window 0x%lx already removed\n", xwindow); return; } - + meta_topic (META_DEBUG_COMPOSITOR, "Removing window 0x%lx (%s) from compositor\n", xwindow, - cwindow->viewable ? "mapped" : "unmapped"); + cwindow_get_viewable (cwindow) ? "mapped" : "unmapped"); - screen = meta_compositor_window_get_screen (cwindow); - - if (cwindow->last_painted_extents) + screen = cwindow_get_screen (cwindow); + + if (cwindow_get_last_painted_extents (cwindow)) { - merge_and_destroy_damage_region (compositor, - screen, - cwindow->last_painted_extents); - cwindow->last_painted_extents = None; + meta_compositor_invalidate_region (compositor, + screen, + cwindow_get_last_painted_extents (cwindow)); + cwindow_set_last_painted_extents (cwindow, None); } screen->compositor_windows = g_list_remove (screen->compositor_windows, @@ -1228,74 +1284,142 @@ meta_compositor_remove_window (MetaCompositor *compositor, #endif /* HAVE_COMPOSITE_EXTENSIONS */ } +static XserverRegion +empty_region (Display *dpy) +{ + XRectangle r; + r.x = 0; + r.y = 0; + r.width = 10; + r.height = 10; + return XFixesCreateRegion (dpy, &r, 1); +} + void meta_compositor_manage_screen (MetaCompositor *compositor, MetaScreen *screen) { #ifdef HAVE_COMPOSITE_EXTENSIONS XRenderPictureAttributes pa; - XRectangle r; XRenderColor c; 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); - + + /* FIXME add flag for whether we're composite-managing each + * screen and detect failure here + */ + + /* CompositeRedirectSubwindows result in all mapped windows + * getting unmapped/mapped, which confuses Metacity since it + * thinks that non-override redirect windows should only + * be mapped by the window manager. + * + * To work around this, we discard all events generated + * by XCompositeRedirectSubwindows + */ + + XSync (screen->display->xdisplay, False); + /* FIXME add flag for whether we're composite-managing each * screen and detect failure here */ XCompositeRedirectSubwindows (screen->display->xdisplay, - screen->xroot, - CompositeRedirectManual); - meta_topic (META_DEBUG_COMPOSITOR, "Subwindows redirected, we are now the compositing manager\n"); + screen->xroot, + CompositeRedirectManual); + XSync (screen->display->xdisplay, True); + + /* FIMXE: This shouldn't be needed for the gl compositor, but it is. Various stuff gets + * confused if root_picture is 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); - - screen->trans_pixmap = XCreatePixmap (compositor->display->xdisplay, - screen->xroot, 1, 1, 8); - - pa.repeat = True; - screen->trans_picture = - XRenderCreatePicture (compositor->display->xdisplay, - screen->trans_pixmap, - XRenderFindStandardFormat (compositor->display->xdisplay, - PictStandardA8), - CPRepeat, - &pa); - - c.red = c.green = c.blue = 0; - c.alpha = 0xc0c0; - XRenderFillRectangle (compositor->display->xdisplay, - PictOpSrc, - screen->trans_picture, &c, 0, 0, 1, 1); - - /* Damage the whole screen */ - r.x = 0; - r.y = 0; - r.width = screen->width; - r.height = screen->height; + XRenderCreatePicture (compositor->display->xdisplay, + screen->xroot, + XRenderFindVisualFormat (compositor->display->xdisplay, + DefaultVisual (compositor->display->xdisplay, + screen->number)), + CPSubwindowMode, + &pa); + + if (USE_RENDER) + { + meta_topic (META_DEBUG_COMPOSITOR, "Subwindows redirected, we are now the compositing manager\n"); + + g_assert (screen->root_picture != None); + + screen->trans_pixmap = XCreatePixmap (compositor->display->xdisplay, + screen->xroot, 1, 1, 8); + + pa.repeat = True; + screen->trans_picture = + XRenderCreatePicture (compositor->display->xdisplay, + screen->trans_pixmap, + XRenderFindStandardFormat (compositor->display->xdisplay, + PictStandardA8), + CPRepeat, + &pa); + + c.red = c.green = c.blue = 0; + c.alpha = 0xd0d0; + XRenderFillRectangle (compositor->display->xdisplay, + PictOpSrc, + screen->trans_picture, &c, 0, 0, 1, 1); + } + else + { +#if 0 + = XCreateSimpleWindow (compositor->display->xdisplay, screen->xroot, 0, 0, + 1600, + 1200, + 0, 0, 0x00FFFF00); +#endif + Window w; - merge_and_destroy_damage_region (compositor, - screen, - XFixesCreateRegion (compositor->display->xdisplay, - &r, 1)); - + GLXContext context; + XSetWindowAttributes attr; + + XVisualInfo *visual = init_gl (compositor->display->xdisplay, 0, &context); + +#if 0 + g_print ("dfepth: %d\n", visual->depth); + g_print ("visual: %lx\n", visual->visualid); +#endif + + attr.colormap = XCreateColormap(compositor->display->xdisplay, screen->xroot, visual->visual, + AllocNone); + + w = XCreateWindow (compositor->display->xdisplay, screen->xroot, 0, 0, + screen->width, screen->height, 0, + visual->depth, InputOutput, visual->visual, + CWColormap, &attr); + + XSetWindowBackgroundPixmap (compositor->display->xdisplay, w, None); + + XserverRegion e = empty_region (compositor->display->xdisplay); + + /* Make the window "OutputOnly" */ + XFixesSetWindowShapeRegion (compositor->display->xdisplay, w, ShapeInput, + 0, 0, e); + + XRaiseWindow (compositor->display->xdisplay, w); + XMapWindow (compositor->display->xdisplay, w); + + XCompositeUnredirectWindow (compositor->display->xdisplay, w, + CompositeRedirectManual); + + glXMakeCurrent (compositor->display->xdisplay, w, context); + + compositor->gl_window = w; + } #endif /* HAVE_COMPOSITE_EXTENSIONS */ } @@ -1306,7 +1430,7 @@ meta_compositor_unmanage_screen (MetaCompositor *compositor, #ifdef HAVE_COMPOSITE_EXTENSIONS if (!compositor->enabled) return; /* no extension */ - + XRenderFreePicture (screen->display->xdisplay, screen->root_picture); screen->root_picture = None; @@ -1319,41 +1443,255 @@ meta_compositor_unmanage_screen (MetaCompositor *compositor, while (screen->compositor_windows != NULL) { - MetaCompositorWindow *cwindow = screen->compositor_windows->data; - + CWindow *cwindow = screen->compositor_windows->data; + meta_topic (META_DEBUG_COMPOSITOR, - "Unmanage screen for 0x%lx\n", cwindow->xwindow); - meta_compositor_remove_window (compositor, cwindow->xwindow); + "Unmanage screen for 0x%lx\n", cwindow_get_xwindow (cwindow)); + meta_compositor_remove_window (compositor, cwindow_get_xwindow (cwindow)); } #endif /* HAVE_COMPOSITE_EXTENSIONS */ } +static CWindow * +window_to_cwindow (MetaCompositor *compositor, + MetaWindow *window) +{ + Window xwindow; + CWindow *cwindow; + + if (window->frame) + xwindow = window->frame->xwindow; + else + xwindow = window->xwindow; + + cwindow = g_hash_table_lookup (compositor->window_hash, + &xwindow); + + return cwindow; +} + void meta_compositor_damage_window (MetaCompositor *compositor, MetaWindow *window) { #ifdef HAVE_COMPOSITE_EXTENSIONS - Window xwindow; - MetaCompositorWindow *cwindow; - + XserverRegion region; + CWindow *cwindow; + if (!compositor->enabled) return; - + if (window->screen->root_picture == None) return; - if (window->frame) - xwindow = window->frame->xwindow; - else - xwindow = window->xwindow; - - cwindow = g_hash_table_lookup (compositor->window_hash, - &xwindow); + cwindow = window_to_cwindow (compositor, window); + if (cwindow == NULL) return; - merge_and_destroy_damage_region (compositor, - window->screen, - window_extents (cwindow)); + region = cwindow_extents (cwindow); + + meta_compositor_invalidate_region (compositor, cwindow_get_screen (cwindow), region); + + XFixesDestroyRegion (compositor->display->xdisplay, region); #endif /* HAVE_COMPOSITE_EXTENSIONS */ } + +void +meta_compositor_stop_compositing (MetaCompositor *compositor, + MetaWindow *window) +{ + CWindow *cwindow; + cwindow = window_to_cwindow (compositor, window); + + if (cwindow) + cwindow_freeze (cwindow); +} + +void +meta_compositor_start_compositing (MetaCompositor *compositor, + MetaWindow *window) +{ + CWindow *cwindow; + cwindow = window_to_cwindow (compositor, window); + + if (cwindow) + cwindow_thaw (cwindow); + + meta_compositor_repair_now (compositor); +} + +static void +quad_to_quad_interpolate (Quad *start, Quad *end, Quad *dest, gdouble t) +{ + int i; + + for (i = 0; i < 4; ++i) + { + if (i == 3) + { + dest->points[i].x = + start->points[i].x + (end->points[i].x - start->points[i].x) * t; + } + else + { + dest->points[i].x = + start->points[i].x + (end->points[i].x - start->points[i].x) * pow (t, 1.5); + } + if (i == 1) + { + dest->points[i].y = + start->points[i].y + (end->points[i].y - start->points[i].y) * pow (t, 1.5); + } + else + { + dest->points[i].y = + start->points[i].y + (end->points[i].y - start->points[i].y) * t; + } + } +} + +void +meta_compositor_genie (MetaCompositor *compositor, + MetaWindow *window) +{ + int i; + int x, y, w, h; + + /* It's unusably slow right now, and also ugly */ + return; + + Quad start1; + Quad end1; + + Quad start2; + Quad end2; + + CWindow *cwindow = window_to_cwindow (compositor, window); + + if (!cwindow) + return; + + x = cwindow_get_x (cwindow); + y = cwindow_get_y (cwindow); + w = cwindow_get_width (cwindow) + cwindow_get_border_width (cwindow) * 2; + h = cwindow_get_height (cwindow) + cwindow_get_border_width (cwindow) * 2; + + start1.points[0].x = x; + start1.points[0].y = y; + start1.points[1].x = x + w/2 - 1; + start1.points[1].y = y; + start1.points[2].x = x; + start1.points[2].y = y + h - 1; + start1.points[3].x = x + w/2 - 1; + start1.points[3].y = y + h - 1; + + end1.points[0].x = 500; + end1.points[0].y = 200 + 50; + end1.points[1].x = 600; + end1.points[1].y = 200 + 50; + end1.points[2].x = 500; + end1.points[2].y = 200 + 80; + end1.points[3].x = 600; + end1.points[3].y = 200 + 80; + + start2.points[0].x = x + w/2; + start2.points[0].y = y; + start2.points[1].x = x + w - 1; + start2.points[1].y = y; + start2.points[2].x = x + w/2; + start2.points[2].y = y + h - 1; + start2.points[3].x = x + w - 1; + start2.points[3].y = y + h - 1; + + end2.points[0].x = 600; + end2.points[0].y = 200 + 50; + end2.points[1].x = 700; + end2.points[1].y = 200 + 50; + end2.points[2].x = 600; + end2.points[2].y = 200 + 80; + end2.points[3].x = 700; + end2.points[3].y = 200 + 130; + +#define STEPS 50 + + for (i = 0; i < STEPS; ++i) + { + Distortion distortion[2]; + Picture buffer; + Pixmap pixmap; + + quad_to_quad_interpolate (&start1, &end1, &distortion[0].destination, i/((double)STEPS - 1)); + quad_to_quad_interpolate (&start2, &end2, &distortion[1].destination, i/((double)STEPS - 1)); + + distortion[0].source.x = 0; + distortion[0].source.y = 0; + distortion[0].source.width = cwindow_get_width (cwindow) / 2; + distortion[0].source.height = cwindow_get_height (cwindow); + + distortion[1].source.x = cwindow_get_width (cwindow) / 2; + distortion[1].source.y = 0; + distortion[1].source.width = cwindow_get_width (cwindow) / 2; + distortion[1].source.height = cwindow_get_height (cwindow); + + cwindow_set_transformation (cwindow, distortion, 2); + + buffer = create_root_buffer (window->screen, &pixmap); + + { + XRectangle r; + XserverRegion region; + + r.x = 0; + r.y = 0; + r.width = window->screen->width; + r.height = window->screen->height; + + region = XFixesCreateRegion (compositor->display->xdisplay, &r, 1); + + XFixesDestroyRegion (compositor->display->xdisplay, + do_paint_screen (compositor, window->screen, buffer, region)); + XFixesDestroyRegion (compositor->display->xdisplay, region); + } + + cwindow_draw (cwindow, buffer, None); + + XFixesSetPictureClipRegion (compositor->display->xdisplay, + window->screen->root_picture, 0, 0, None); + /* Copy buffer to root window */ + paint_buffer (window->screen, pixmap, None); + + XFreePixmap (compositor->display->xdisplay, pixmap); + + XRenderFreePicture (compositor->display->xdisplay, buffer); + + XSync (compositor->display->xdisplay, False); + } + + cwindow_set_transformation (cwindow, NULL, 0); +} + +MetaDisplay * +meta_compositor_get_display (MetaCompositor *compositor) +{ + return compositor->display; +} + +void +meta_compositor_set_translucent (MetaCompositor *compositor, + MetaWindow *window, + gboolean translucent) +{ + CWindow *cwindow = window_to_cwindow (compositor, window); + + if (cwindow) + { + cwindow_set_translucent (cwindow, translucent); + } +} + +XID +meta_compositor_get_gl_window (MetaCompositor *compositor) +{ + return compositor->gl_window; +} diff --git a/src/compositor.h b/src/compositor.h index d1e824b3..1426a314 100644 --- a/src/compositor.h +++ b/src/compositor.h @@ -25,6 +25,8 @@ #include "util.h" #include "display.h" +#include <X11/extensions/Xfixes.h> + MetaCompositor* meta_compositor_new (MetaDisplay *display); void meta_compositor_unref (MetaCompositor *compositor); void meta_compositor_process_event (MetaCompositor *compositor, @@ -35,6 +37,8 @@ void meta_compositor_add_window (MetaCompositor *compositor, XWindowAttributes *attrs); void meta_compositor_remove_window (MetaCompositor *compositor, Window xwindow); +void meta_compositor_set_debug_updates (MetaCompositor *compositor, + gboolean debug_updates); void meta_compositor_manage_screen (MetaCompositor *compositor, MetaScreen *screen); @@ -44,13 +48,28 @@ void meta_compositor_unmanage_screen (MetaCompositor *compositor, void meta_compositor_damage_window (MetaCompositor *compositor, MetaWindow *window); -#endif /* META_COMPOSITOR_H */ - - - - - - - - +void meta_compositor_stop_compositing (MetaCompositor *compositor, + MetaWindow *window); +void meta_compositor_start_compositing (MetaCompositor *compositor, + MetaWindow *window); +MetaDisplay *meta_compositor_get_display (MetaCompositor *compositor); + +void +meta_compositor_genie (MetaCompositor *compositor, + MetaWindow *window); +void +meta_compositor_invalidate_region (MetaCompositor *compositor, + MetaScreen *screen, + XserverRegion invalid_area); + +void +meta_compositor_set_translucent (MetaCompositor *compositor, + MetaWindow *window, + gboolean translucent); +gboolean +meta_compositor_repair_now (MetaCompositor *compositor); + +XID +meta_compositor_get_gl_window (MetaCompositor *compositor); +#endif /* META_COMPOSITOR_H */ diff --git a/src/cwindow.c b/src/cwindow.c new file mode 100644 index 00000000..803248cf --- /dev/null +++ b/src/cwindow.c @@ -0,0 +1,1639 @@ +#include <sys/types.h> +#include <sys/shm.h> +#include <X11/Xlib.h> +#include "cwindow.h" +#include "errors.h" +#include "compositor.h" +#include "matrix.h" +#include <X11/extensions/Xcomposite.h> +#include <math.h> +#include <string.h> +#include <X11/extensions/shape.h> +#include <X11/extensions/XShm.h> +#include "lmctexture.h" +#include "window.h" + + +#define SHADOW_RADIUS 6 +#define SHADOW_OPACITY (.25) +#define SHADOW_OFFSET -6 + +static Picture shadow_picture (Display *dpy, Window root, + double opacity, int width, int height, int *wp, int *hp); + +typedef struct Geometry Geometry; +typedef struct FreezeInfo FreezeInfo; + +struct Geometry +{ + int x; + int y; + int width; + int height; + int border_width; +}; + +struct FreezeInfo +{ + Geometry geometry; + Pixmap pixmap; +}; + +/* Unlike MetaWindow, there's one of these for _all_ toplevel windows, + * override redirect or not. We also track unmapped windows as + * otherwise on window map we'd have to determine where the + * newly-mapped window was in the stack. A MetaCompositorWindow may + * correspond to a metacity window frame rather than an application + * window. + */ +struct CWindow +{ + Window xwindow; + +#ifdef HAVE_COMPOSITE_EXTENSIONS + MetaCompositor *compositor; + + Geometry geometry; + + Damage damage; + XserverRegion last_painted_extents; + + XserverRegion border_size; + + Picture shadow; + + FreezeInfo *freeze_info; + + unsigned int managed : 1; + unsigned int damaged : 1; + unsigned int viewable : 1; + unsigned int input_only : 1; + unsigned int translucent : 1; + + unsigned int screen_index : 8; + + Visual *visual; + + Distortion *distortions; + int n_distortions; + + int shadow_width; + int shadow_height; + + int depth; + XImage *image; + XShmSegmentInfo shm_info; + Pixmap shm_pixmap; + GC shm_gc; + LmcBits *bits; + XserverRegion parts_region; + LmcTexture *texture; + + long damage_serial; +#endif +}; + +static Display *cwindow_get_xdisplay (CWindow *cwindow); + +#ifdef HAVE_COMPOSITE_EXTENSIONS +void +cwindow_free (CWindow *cwindow) +{ + g_assert (cwindow->damage != None); + + meta_error_trap_push (meta_compositor_get_display (cwindow->compositor)); + /* This seems to cause an error if the window + * is destroyed? + */ + g_print ("destroying damage for %lx\n", cwindow->xwindow); + XDamageDestroy (cwindow_get_xdisplay (cwindow), cwindow->damage); + + /* Free our window pixmap name */ + if (cwindow->freeze_info) + cwindow_thaw (cwindow); + + meta_error_trap_pop (meta_compositor_get_display (cwindow->compositor), + FALSE); + + g_free (cwindow); +} +#endif /* HAVE_COMPOSITE_EXTENSIONS */ + +static void +create_shadow (CWindow *cwindow) +{ + if (USE_RENDER && !cwindow->shadow) + { + cwindow->shadow = shadow_picture (cwindow_get_xdisplay (cwindow), + cwindow_get_screen (cwindow)->xroot, + SHADOW_OPACITY, + cwindow_get_width (cwindow), + cwindow_get_height (cwindow), + &cwindow->shadow_width, &cwindow->shadow_height); + } +} + +static void +delete_shadow (CWindow *cwindow) +{ + if (USE_RENDER && cwindow->shadow) + { + XRenderFreePicture (cwindow_get_xdisplay (cwindow), cwindow->shadow); + cwindow->shadow = None; + } +} + +#ifdef HAVE_COMPOSITE_EXTENSIONS +XserverRegion +cwindow_extents (CWindow *cwindow) +{ + Geometry *geometry; + XRectangle r; + + if (cwindow->freeze_info) + geometry = &cwindow->freeze_info->geometry; + else + geometry = &cwindow->geometry; + + r.x = geometry->x; + r.y = geometry->y; + r.width = geometry->width; + r.height = geometry->height; + + create_shadow (cwindow); + + if (cwindow->shadow) + { + r.x = geometry->x + SHADOW_OFFSET; + r.y = geometry->y; + r.width = cwindow->shadow_width; + r.height = cwindow->shadow_height; + } + + return XFixesCreateRegion (cwindow_get_xdisplay (cwindow), &r, 1); +} +#endif /* HAVE_COMPOSITE_EXTENSIONS */ + +static gboolean +cwindow_has_alpha (CWindow *cwindow) +{ + if (USE_RENDER) + { + XRenderPictFormat *format; + + format = XRenderFindVisualFormat (cwindow_get_xdisplay (cwindow), + cwindow_get_visual (cwindow)); + + return format->direct.alpha; + } + else + { + return TRUE; + } +} + +XserverRegion +cwindow_get_opaque_region (CWindow *cwindow) +{ + if (!USE_RENDER || cwindow->translucent || cwindow_get_input_only (cwindow) || + !cwindow_get_viewable (cwindow) || cwindow_has_alpha (cwindow) || + cwindow->n_distortions) + { + return XFixesCreateRegion (cwindow_get_xdisplay (cwindow), NULL, 0); + } + else + { + XserverRegion region = XFixesCreateRegionFromWindow ( + cwindow_get_xdisplay (cwindow), + cwindow->xwindow, ShapeBounding); + + XFixesTranslateRegion (cwindow_get_xdisplay (cwindow), + region, + cwindow_get_x (cwindow), + cwindow_get_y (cwindow)); + + return region; + } +} + +static void +cwindow_queue_paint (CWindow *cwindow) +{ + XserverRegion region; + MetaScreen *screen; + + create_shadow (cwindow); + + region = cwindow_extents (cwindow); + screen = cwindow_get_screen (cwindow); + + meta_compositor_invalidate_region (cwindow->compositor, + screen, + region); + + XFixesDestroyRegion (cwindow_get_xdisplay (cwindow), region); +} + +void +cwindow_set_translucent (CWindow *cwindow, gboolean translucent) +{ +#if 0 + cwindow->translucent = !!translucent; +#endif + + cwindow_queue_paint (cwindow); +} + +Drawable +cwindow_get_drawable (CWindow *cwindow) +{ + if (cwindow->freeze_info) + { + return cwindow->freeze_info->pixmap; + } + else + { + return cwindow->xwindow; + } +} + +void +cwindow_destroy_last_painted_extents (CWindow *cwindow) +{ + if (cwindow->last_painted_extents) + { + XFixesDestroyRegion (cwindow_get_xdisplay (cwindow), cwindow->last_painted_extents); + cwindow->last_painted_extents = None; + } +} + +void +cwindow_set_last_painted_extents (CWindow *cwindow, XserverRegion extents) +{ + cwindow_destroy_last_painted_extents (cwindow); + cwindow->last_painted_extents = extents; +} + +MetaScreen* +cwindow_get_screen (CWindow *cwindow) +{ + MetaScreen *screen; + GSList *tmp; + + screen = NULL; + tmp = meta_compositor_get_display (cwindow->compositor)->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; +} + +/* From luminocity */ +static void +destroy_bits (gpointer data) +{ + XShmSegmentInfo *shm_info = data; + + shmdt (shm_info->shmaddr); + + g_free (shm_info); +} + +static gboolean +create_window_image (CWindow *cwindow) +{ + Display *xdisplay = cwindow_get_xdisplay (cwindow); + int image_size; + XGCValues gcv; + LmcBitsFormat format; + Geometry *geometry = cwindow->freeze_info? + &cwindow->freeze_info->geometry : + &cwindow->geometry; + + g_return_val_if_fail (cwindow->image == NULL, False); + + if (cwindow->depth == 16 && + cwindow->visual->red_mask == 0xf800 && + cwindow->visual->green_mask == 0x7e0 && + cwindow->visual->blue_mask == 0x1f) + { + format = LMC_BITS_RGB_16; + } + else if (cwindow->depth == 24 && + cwindow->visual->red_mask == 0xff0000 && + cwindow->visual->green_mask == 0xff00 && + cwindow->visual->blue_mask == 0xff) + { + format = LMC_BITS_RGB_32; + } + else if (cwindow->depth == 32 && + cwindow->visual->red_mask == 0xff0000 && + cwindow->visual->green_mask == 0xff00 && + cwindow->visual->blue_mask == 0xff) + { + format = LMC_BITS_ARGB_32; + } + else + { + g_warning ("Unknown visual format depth=%d, r=%#lx/g=%#lx/b=%#lx", + cwindow->depth, cwindow->visual->red_mask, cwindow->visual->green_mask, cwindow->visual->blue_mask); + + return FALSE; + } + + cwindow->image = XCreateImage (xdisplay, + cwindow->visual, + cwindow->depth, + ZPixmap, + 0, + 0, + geometry->width, + geometry->height, + 32, + 0); + if (!cwindow->image) + return FALSE; + + image_size = (cwindow->image->bytes_per_line * geometry->height); + + cwindow->shm_info.shmid = shmget (IPC_PRIVATE, image_size, IPC_CREAT|0600); + if (cwindow->shm_info.shmid < 0) + { + XDestroyImage (cwindow->image); + cwindow->image = NULL; + return FALSE; + } + + cwindow->shm_info.shmaddr = (char *) shmat (cwindow->shm_info.shmid, 0, 0); + if (cwindow->shm_info.shmaddr == ((char *) -1)) + { + XDestroyImage (cwindow->image); + cwindow->image = NULL; + shmctl (cwindow->shm_info.shmid, IPC_RMID, 0); + return FALSE; + } + + meta_error_trap_push_with_return (meta_compositor_get_display (cwindow->compositor)); + + cwindow->shm_info.readOnly = False; + XShmAttach (xdisplay, &cwindow->shm_info); + XSync (xdisplay, False); + + if (meta_error_trap_pop_with_return (meta_compositor_get_display (cwindow->compositor), FALSE)) + { + XDestroyImage (cwindow->image); + cwindow->image = NULL; + shmdt (&cwindow->shm_info.shmaddr); + shmctl (cwindow->shm_info.shmid, IPC_RMID, 0); + return FALSE; + } + + /* Detach now so we clean up on abnormal exit */ + shmctl (cwindow->shm_info.shmid, IPC_RMID, 0); + + cwindow->image->data = cwindow->shm_info.shmaddr; + cwindow->image->obdata = (char *) &cwindow->shm_info; + cwindow->shm_pixmap = XShmCreatePixmap (xdisplay, + cwindow->xwindow, + cwindow->image->data, + &cwindow->shm_info, + geometry->width, + geometry->height, + cwindow->depth); + + gcv.graphics_exposures = False; + gcv.subwindow_mode = IncludeInferiors; + cwindow->shm_gc = XCreateGC (xdisplay, + cwindow->xwindow, + GCGraphicsExposures|GCSubwindowMode, + &gcv); + + cwindow->bits = lmc_bits_new (format, + geometry->width, geometry->height, + cwindow->image->data, + cwindow->image->bytes_per_line, + destroy_bits, + g_memdup (&cwindow->shm_info, sizeof (XShmSegmentInfo))); + + return TRUE; +} + +static void +initialize_damage (CWindow *cwindow) +{ + create_window_image (cwindow); + + cwindow->damage_serial = 0; + + cwindow_queue_paint (cwindow); +} + +CWindow * +cwindow_new (MetaCompositor *compositor, + Window xwindow, + XWindowAttributes *attrs) +{ + CWindow *cwindow; + Damage damage; + gboolean is_gl_window; + + /* Create Damage object to monitor window damage */ + meta_error_trap_push (meta_compositor_get_display (compositor)); + g_print ("creating damage for %lx\n", xwindow); + + is_gl_window = (xwindow == meta_compositor_get_gl_window (compositor)); + + if (!is_gl_window) + { + damage = XDamageCreate (meta_compositor_get_display (compositor)->xdisplay, + xwindow, XDamageReportNonEmpty); + } + else + { + damage = None; + } + + meta_error_trap_pop (meta_compositor_get_display (compositor), FALSE); + + if (damage == None && !is_gl_window) + return NULL; + + cwindow = g_new0 (CWindow, 1); + + cwindow->compositor = compositor; + cwindow->xwindow = xwindow; + cwindow->screen_index = XScreenNumberOfScreen (attrs->screen); + cwindow->damage = damage; + cwindow->depth = attrs->depth; + + if (is_gl_window) + { + return cwindow; + } + + cwindow->freeze_info = NULL; + + cwindow->geometry.x = attrs->x; + cwindow->geometry.y = attrs->y; + cwindow->geometry.width = attrs->width; + cwindow->geometry.height = attrs->height; + cwindow->geometry.border_width = attrs->border_width; + + if (attrs->class == InputOnly) + cwindow->input_only = TRUE; + else + cwindow->input_only = FALSE; + + cwindow->visual = attrs->visual; + + /* viewable == mapped for the root window, since root can't be unmapped */ + cwindow->viewable = (attrs->map_state == IsViewable); + + cwindow->parts_region = XFixesCreateRegion (cwindow_get_xdisplay (cwindow), 0, 0); + + if (!cwindow->input_only) + initialize_damage (cwindow); + + return cwindow; +} + +XID * +cwindow_get_xid_address (CWindow *cwindow) +{ + return &cwindow->xwindow; +} + +static Display * +cwindow_get_xdisplay (CWindow *cwindow) +{ + return meta_compositor_get_display (cwindow->compositor)->xdisplay; +} + + +Window +cwindow_get_xwindow (CWindow *cwindow) +{ + return cwindow->xwindow; +} + +gboolean +cwindow_get_viewable (CWindow *cwindow) +{ + return cwindow->viewable; +} + +gboolean +cwindow_get_input_only (CWindow *cwindow) +{ + return cwindow->input_only; +} + +Visual * +cwindow_get_visual (CWindow *cwindow) +{ + return cwindow->visual; +} + +XserverRegion +cwindow_get_last_painted_extents (CWindow *cwindow) +{ + return cwindow->last_painted_extents; +} + + +int +cwindow_get_x (CWindow *cwindow) +{ + return cwindow->geometry.x; +} + +int +cwindow_get_y (CWindow *cwindow) +{ + return cwindow->geometry.y; +} + +int +cwindow_get_width (CWindow *cwindow) +{ + return cwindow->geometry.width; +} + +int +cwindow_get_height (CWindow *cwindow) +{ + return cwindow->geometry.height; +} + +int +cwindow_get_border_width (CWindow *cwindow) +{ + return cwindow->geometry.border_width; +} + +Damage +cwindow_get_damage (CWindow *cwindow) +{ + return cwindow->damage; +} + +MetaCompositor * +cwindow_get_compositor (CWindow *cwindow) +{ + return cwindow->compositor; +} + +void +cwindow_set_x (CWindow *cwindow, int x) +{ + cwindow->geometry.x = x; +} + +void +cwindow_set_y (CWindow *cwindow, int y) +{ + cwindow->geometry.y = y; +} + +void +cwindow_set_width (CWindow *cwindow, int width) +{ + cwindow->geometry.width = width; +} + +void +cwindow_set_height (CWindow *cwindow, int height) +{ + cwindow->geometry.height = height; +} + + +void +cwindow_set_viewable (CWindow *cwindow, gboolean viewable) +{ + viewable = !!viewable; + if (cwindow->viewable != viewable) + { + cwindow_queue_paint (cwindow); + cwindow->viewable = viewable; + } +} + +void +cwindow_set_border_width (CWindow *cwindow, int border_width) +{ + cwindow->geometry.border_width = border_width; +} + +static XFixed +double_to_fixed (gdouble d) +{ + return XDoubleToFixed (d); +} + +static void +convert_matrix (Matrix3 *matrix, XTransform *trans) +{ +#if 0 + matrix3_transpose (matrix); +#endif + trans->matrix[0][0] = double_to_fixed (matrix->coeff[0][0]); + trans->matrix[1][0] = double_to_fixed (matrix->coeff[1][0]); + trans->matrix[2][0] = double_to_fixed (matrix->coeff[2][0]); + trans->matrix[0][1] = double_to_fixed (matrix->coeff[0][1]); + trans->matrix[1][1] = double_to_fixed (matrix->coeff[1][1]); + trans->matrix[2][1] = double_to_fixed (matrix->coeff[2][1]); + trans->matrix[0][2] = double_to_fixed (matrix->coeff[0][2]); + trans->matrix[1][2] = double_to_fixed (matrix->coeff[1][2]); + trans->matrix[2][2] = double_to_fixed (matrix->coeff[2][2]); +} + +gboolean +cwindow_is_translucent (CWindow *cwindow) +{ + return FALSE; + MetaCompositor *compositor = cwindow_get_compositor (cwindow); + MetaWindow *window = meta_display_lookup_x_window (meta_compositor_get_display (compositor), cwindow_get_xwindow (cwindow)); + return (window != NULL && + window == meta_compositor_get_display (compositor)->grab_window && + (meta_grab_op_is_resizing (meta_compositor_get_display (compositor)->grab_op) || + meta_grab_op_is_moving (meta_compositor_get_display (compositor)->grab_op))); +} + + +static XRectangle +bbox (Quad *q) +{ + int x1, x2, y1, y2; + XRectangle result; + int i; + + x2 = x1 = q->points[0].x; + y2 = y1 = q->points[0].y; + + for (i = 0; i < 4; ++i) + x1 = MIN (x1, q->points[i].x); + + for (i = 0; i < 4; ++i) + y1 = MIN (y1, q->points[i].y); + + for (i = 0; i < 4; ++i) + x2 = MAX (x2, q->points[i].x); + + for (i = 0; i < 4; ++i) + y2 = MAX (y2, q->points[i].y); + + result.x = x1; + result.y = y1; + result.width = x2 - x1 + 1; + result.height = y2 - y1 + 1; + +#if 0 + g_print ("bbox: %d %d %d %d\n", result.x, result.y, result.width, result.height); +#endif + + return result; +} + +static void +compute_transform (int x, int y, + int width, int height, + Quad *destination, + XTransform *transform) +{ + int tx, ty; + int i; + Quad tmp = *destination; + Matrix3 matrix; + + /* Translate destination so it starts in (x, y); + * + * We will position it correctly with the composite request + * Coordinates are source coordinates + * + * I believe this is a hackaround a bug in render transformation (basically + * it translates source coordinates, not destination as it's supposed to). + */ + tx = bbox (&tmp).x - x; + ty = bbox (&tmp).y - y; + + for (i = 0; i < 4; ++i) + { + tmp.points[i].x -= tx; + tmp.points[i].y -= ty; + } + + /* Compute the matrix */ + matrix3_identity (&matrix); + +#if 0 + matrix3_translate (&matrix, (gdouble)-x, (gdouble)-y); +#endif + +#if 0 + g_print ("mapping from %d %d %d %d to (%d %d) (%d %d) (%d %d) (%d %d)\n", x, y, width, height, + tmp.points[0].x, tmp.points[0].y, + tmp.points[1].x, tmp.points[1].y, + tmp.points[2].x, tmp.points[2].y, + tmp.points[3].x, tmp.points[3].y); +#endif + + transform_matrix_perspective (x, y, + x + width - 1, + y + height - 1, + + tmp.points[0].x, tmp.points[0].y, + tmp.points[1].x, tmp.points[1].y, + tmp.points[2].x, tmp.points[2].y, + tmp.points[3].x, tmp.points[3].y, + + &matrix); + + matrix3_invert (&matrix); + + /* convert to XTransform */ + convert_matrix (&matrix, transform); +} + +static void +print_region (Display *dpy, const char *name, XserverRegion region) +{ + XRectangle *rects; + int i, n_rects; + + rects = XFixesFetchRegion (dpy, region, &n_rects); + + g_print ("region \"%s\":\n", name); + for (i = 0; i < n_rects; ++i) + g_print (" %d %d %d %d\n", rects[i].x, rects[i].y, rects[i].width, rects[i].height); + XFree (rects); +} + +void +cwindow_process_damage_notify (CWindow *cwindow, XDamageNotifyEvent *event) +{ + MetaScreen *screen; + MetaWindow *window; + + window = meta_display_lookup_x_window (meta_compositor_get_display (cwindow->compositor), + cwindow_get_xwindow (cwindow)); +#if 0 + if (window) + g_print ("damage on %lx (%s)\n", cwindow->xwindow, window->title); + else + g_print ("damage on unknown window\n"); +#endif + + screen = cwindow_get_screen (cwindow); + + if (cwindow->xwindow == screen->xroot) + g_print ("huh????\n"); + + if (USE_RENDER) + { + XserverRegion region; + region = XFixesCreateRegion (cwindow_get_xdisplay (cwindow), NULL, 0); + + /* translate region to screen; can error if window of damage is + * destroyed + */ + meta_error_trap_push (meta_compositor_get_display (cwindow->compositor)); + XDamageSubtract (cwindow_get_xdisplay (cwindow), + cwindow_get_damage (cwindow), None, region); + meta_error_trap_pop (meta_compositor_get_display (cwindow->compositor), FALSE); + + XFixesTranslateRegion (cwindow_get_xdisplay (cwindow), + region, + cwindow_get_x (cwindow), + cwindow_get_y (cwindow)); + + if (!cwindow->freeze_info) + { + meta_compositor_invalidate_region (cwindow->compositor, screen, region); + } + + XFixesDestroyRegion (cwindow_get_xdisplay (cwindow), region); + /* ignore damage on frozen window */ + } + else + { + XserverRegion region; + + region = XFixesCreateRegion (cwindow_get_xdisplay (cwindow), NULL, 0); + + meta_error_trap_push (meta_compositor_get_display (cwindow->compositor)); +#if 0 + g_print ("cleaning damage for %lx\n", cwindow->xwindow); +#endif + XDamageSubtract (cwindow_get_xdisplay (cwindow), + cwindow_get_damage (cwindow), + None, + region); + meta_error_trap_pop (meta_compositor_get_display (cwindow->compositor), FALSE); + + XFixesUnionRegion (cwindow_get_xdisplay (cwindow), + cwindow->parts_region, cwindow->parts_region, region); + +#if 0 + print_region (cwindow_get_xdisplay (cwindow), "parts region is now", cwindow->parts_region); +#endif + + XFixesTranslateRegion (cwindow_get_xdisplay (cwindow), + region, + cwindow_get_x (cwindow), + cwindow_get_y (cwindow)); + + meta_compositor_invalidate_region (cwindow->compositor, screen, region); + + XFixesDestroyRegion (cwindow_get_xdisplay (cwindow), region); + +#if 0 + XFixesDestroyRegion (cwindow_get_xdisplay (cwindow), region); + + rect.width = screen->width; + rect.height = screen->height; + rect.x = 0; + rect.y = 0; + + region = XFixesCreateRegion (cwindow_get_xdisplay (cwindow), &rect, 1); + +#endif + } +} + +static void +destroy_window_image (CWindow *cwindow); + +void +cwindow_process_configure_notify (CWindow *cwindow, XConfigureEvent *event) +{ + MetaScreen *screen; + int old_width, old_height; + + screen = cwindow_get_screen (cwindow); + + if (cwindow->xwindow == meta_compositor_get_gl_window (cwindow->compositor)) + return; + + if (cwindow_get_last_painted_extents (cwindow) && !cwindow->freeze_info) + { + meta_compositor_invalidate_region (cwindow->compositor, screen, cwindow_get_last_painted_extents (cwindow)); + cwindow_set_last_painted_extents (cwindow, None); + } + + cwindow_set_x (cwindow, event->x); + cwindow_set_y (cwindow, event->y); + old_width = cwindow_get_width (cwindow); + cwindow_set_width (cwindow, event->width); + old_height = cwindow_get_height (cwindow); + cwindow_set_height (cwindow, event->height); + cwindow_set_border_width (cwindow, event->border_width); + + if (cwindow->freeze_info) + { + return; + } + else + { + if (old_width != cwindow_get_width (cwindow) || + old_height != cwindow_get_height (cwindow)) + { + if (cwindow->texture) + { + lmc_texture_unref (cwindow->texture); + cwindow->texture = NULL; + } + + destroy_window_image (cwindow); + initialize_damage (cwindow); + } + +#if 0 + if (USE_RENDER) +#endif + cwindow_queue_paint (cwindow); + } +} + +void +cwindow_set_transformation (CWindow *cwindow, + const Distortion *distortions, + int n_distortions) +{ + if (cwindow->distortions) + { + g_free (cwindow->distortions); + cwindow->distortions = NULL; + cwindow->n_distortions = 0; + } + + if (n_distortions) + { + cwindow->distortions = g_memdup (distortions, n_distortions * sizeof (Distortion)); + cwindow->n_distortions = n_distortions; + } +} + +void +cwindow_freeze (CWindow *cwindow) +{ + if (!cwindow) + return; + + if (cwindow->freeze_info) + { + meta_print_backtrace(); + return; + } + + meta_error_trap_push (meta_compositor_get_display (cwindow->compositor)); + + cwindow->freeze_info = g_new (FreezeInfo, 1); + cwindow->freeze_info->geometry = cwindow->geometry; + + cwindow->freeze_info->pixmap = + XCompositeNameWindowPixmap (cwindow_get_xdisplay (cwindow), + cwindow->xwindow); + + meta_error_trap_pop (meta_compositor_get_display (cwindow->compositor), FALSE); +} + +void +cwindow_thaw (CWindow *cwindow) +{ + if (!cwindow->freeze_info) + return; + + if (cwindow_get_last_painted_extents (cwindow)) + { + meta_compositor_invalidate_region (cwindow->compositor, + cwindow_get_screen (cwindow), + cwindow_get_last_painted_extents (cwindow)); + cwindow_set_last_painted_extents (cwindow, None); + } + + if (!USE_RENDER) + { + + if (cwindow->texture) + { + lmc_texture_unref (cwindow->texture); + cwindow->texture = NULL; + } + + destroy_window_image (cwindow); + initialize_damage (cwindow); + } + else + { + if (cwindow->freeze_info->pixmap) + XFreePixmap (cwindow_get_xdisplay (cwindow), + cwindow->freeze_info->pixmap); + } + + g_free (cwindow->freeze_info); + cwindow->freeze_info = NULL; + + delete_shadow (cwindow); + + cwindow_queue_paint (cwindow); +} + +static void +cwindow_undamage (CWindow *cwindow); + + +static GdkRectangle * +server_region_to_gdk_rects (Display *dpy, XserverRegion region, int *n_rects) +{ + int dummy, i; + XRectangle *xrects; + GdkRectangle *gdk_rects; + + if (!n_rects) + n_rects = &dummy; + + if (!region) + { + *n_rects = 0; + return NULL; + } + + xrects = XFixesFetchRegion (dpy, region, n_rects); + + gdk_rects = g_new (GdkRectangle, *n_rects); + + for (i = 0; i < *n_rects; ++i) + { + gdk_rects[i].x = xrects[i].x; + gdk_rects[i].y = xrects[i].y; + gdk_rects[i].width = xrects[i].width; + gdk_rects[i].height = xrects[i].height; + } + + return gdk_rects; +} + +static void +destroy_window_image (CWindow *cwindow) +{ + Display *xdisplay = cwindow_get_xdisplay (cwindow); + + if (cwindow->image) + { + XShmDetach (xdisplay, &cwindow->shm_info); + XSync (xdisplay, False); + + cwindow->image->data = NULL; + cwindow->image->obdata = NULL; + XDestroyImage (cwindow->image); + cwindow->image = NULL; + + XFreePixmap (xdisplay, cwindow->shm_pixmap); + cwindow->shm_pixmap = None; + + XFreeGC (xdisplay, cwindow->shm_gc); + cwindow->shm_gc = None; + + lmc_bits_unref (cwindow->bits); + cwindow->bits = NULL; + } +} + +void +cwindow_draw (CWindow *cwindow, + Picture destination, + XserverRegion clip_region) +{ + XRenderPictFormat *format; + int i; + Picture picture; + XRenderPictureAttributes pa; + + if (cwindow_get_input_only (cwindow)) + return; + + if (!cwindow_get_viewable (cwindow)) + return; + + if (USE_RENDER) + { + Display *xdisplay = cwindow_get_xdisplay (cwindow); + + XFixesSetPictureClipRegion (xdisplay, picture, 0, 0, clip_region); + format = XRenderFindVisualFormat (xdisplay, + cwindow_get_visual (cwindow)); + + pa.subwindow_mode = IncludeInferiors; + picture = XRenderCreatePicture (xdisplay, + cwindow_get_drawable (cwindow), + format, + CPSubwindowMode, + &pa); + + if (cwindow->n_distortions) + { + for (i = 0; i < cwindow->n_distortions; ++i) + { + XTransform transform; + + Distortion *dist = &cwindow->distortions[i]; + compute_transform (dist->source.x, + dist->source.y, + dist->source.width, dist->source.height, + &dist->destination, &transform); + + /* Draw window */ + XRenderSetPictureTransform (xdisplay, picture, &transform); + XRenderSetPictureFilter (xdisplay, picture, "bilinear", 0, 0); + + XRenderComposite (xdisplay, + PictOpOver, /* PictOpOver for alpha, PictOpSrc without */ + picture, + cwindow_get_screen (cwindow)->trans_picture, + destination, + dist->source.x, dist->source.y, + 0, 0, + bbox (&dist->destination).x, bbox (&dist->destination).y, + bbox (&dist->destination).width, bbox (&dist->destination).height); + } + } + else + { + Display *dpy = xdisplay; + Geometry *geometry = cwindow->freeze_info? + &cwindow->freeze_info->geometry : + &cwindow->geometry; + + XserverRegion shadow_clip; + XserverRegion old_clip = XFixesCreateRegionFromPicture (dpy, destination); + + shadow_clip = cwindow_get_opaque_region (cwindow); + + XFixesSubtractRegion (dpy, shadow_clip, old_clip, shadow_clip); + + XFixesSetPictureClipRegion (dpy, destination, 0, 0, shadow_clip); + + if (!cwindow->translucent && !cwindow_has_alpha (cwindow)) + { + create_shadow (cwindow); + + XRenderComposite (xdisplay, PictOpOver, + cwindow->shadow, None, destination, + 0, 0, + 0, 0, + geometry->x + SHADOW_OFFSET, geometry->y, + cwindow->shadow_width, cwindow->shadow_height); + } + + XFixesSetPictureClipRegion (dpy, destination, 0, 0, old_clip); + + XRenderComposite (xdisplay, + PictOpOver, /* PictOpOver for alpha, PictOpSrc without */ + picture, + cwindow->translucent? cwindow_get_screen (cwindow)->trans_picture : None, + destination, + 0, 0, 0, 0, + geometry->x, + geometry->y, + geometry->width, + geometry->height); + + XFixesDestroyRegion (dpy, old_clip); + XFixesDestroyRegion (dpy, shadow_clip); + } + } + else + { + GdkRectangle *rects; + int n_rects; + GdkRegion *gdk_clip_region; + + Geometry *geometry = cwindow->freeze_info? + &cwindow->freeze_info->geometry : + &cwindow->geometry; + +#if 0 + g_print ("drawing: %lx\n", cwindow->xwindow); +#endif + + cwindow_undamage (cwindow); + + if (!cwindow->texture) + { + cwindow->texture = lmc_texture_new (cwindow->bits); + } + + g_assert (cwindow->texture); + + rects = server_region_to_gdk_rects (cwindow_get_xdisplay (cwindow), + clip_region, &n_rects); + + gdk_clip_region = gdk_region_new (); + + for (i = 0; i < n_rects; ++i) + gdk_region_union_with_rect (gdk_clip_region, &rects[i]); + + if (rects) + { + gdk_region_offset (gdk_clip_region, -geometry->x, -geometry->y); + lmc_texture_draw (cwindow_get_screen (cwindow), cwindow->texture, 1.0, geometry->x, geometry->y, gdk_clip_region); + gdk_region_offset (gdk_clip_region, geometry->x, geometry->y); + } + else + lmc_texture_draw (cwindow_get_screen (cwindow), + cwindow->texture, 1.0, geometry->x, geometry->y, NULL); + + gdk_region_destroy (gdk_clip_region); + } + + if (cwindow_get_last_painted_extents (cwindow)) + cwindow_destroy_last_painted_extents (cwindow); + + cwindow_set_last_painted_extents (cwindow, cwindow_extents (cwindow)); + + if (USE_RENDER) + XRenderFreePicture (cwindow_get_xdisplay (cwindow), picture); +} + + +static void +cwindow_undamage (CWindow *cwindow) +{ + Display *xdisplay = cwindow_get_xdisplay (cwindow); + Window xwindow = cwindow->xwindow; + gboolean get_all; + Geometry *geometry = cwindow->freeze_info? + &cwindow->freeze_info->geometry : + &cwindow->geometry; + + if (!cwindow->image) + return; + + if (cwindow->input_only) + return; + + if (!cwindow->viewable) + return; + + /* If we've already undamaged at least once at this size, just get the + * part that changed. Otherwise, we get everything. + */ + get_all = cwindow->damage_serial == 0; + cwindow->damage_serial = NextRequest (xdisplay); + + /* Window could go away at any point */ + if (cwindow->damage) + { + } + else + g_print ("No damage\n"); + +#if 0 + get_all = TRUE; +#endif + + if (get_all) + { + if (cwindow->texture) + { + lmc_texture_unref (cwindow->texture); + cwindow->texture = NULL; + } + + destroy_window_image (cwindow); + create_window_image (cwindow); + } + + if (!get_all) + XFixesSetGCClipRegion (xdisplay, cwindow->shm_gc, + 0, 0, cwindow->parts_region); + else + XFixesSetGCClipRegion (xdisplay, cwindow->shm_gc, + 0, 0, None); + +#if 0 + g_print ("copying: %lx\n", cwindow->xwindow); +#endif + XCopyArea (xdisplay, + cwindow_get_drawable (cwindow), + cwindow->shm_pixmap, + cwindow->shm_gc, + 0, 0, + geometry->width, geometry->height, + 0, 0); + + XSync (xdisplay, False); + + if (!get_all && cwindow->texture) + { + GdkRectangle *rects; + int n_rects, i; + + rects = server_region_to_gdk_rects (cwindow_get_xdisplay (cwindow), + cwindow->parts_region, &n_rects); + + for (i = 0; i < n_rects; ++i) + { + lmc_texture_update_rect (cwindow->texture, &(rects[i])); + + } + } + + XFixesDestroyRegion (cwindow_get_xdisplay (cwindow), cwindow->parts_region); + cwindow->parts_region = XFixesCreateRegion (cwindow_get_xdisplay (cwindow), 0, 0); +} + + +/* + * Gaussian shadows + */ + +/* + * FIXME: move these into MetaDisplay + */ + +/* For shadow precomputation */ +typedef struct _conv { + int size; + double *data; +} conv; + +int Gsize = -1; +unsigned char *shadowCorner = NULL; +unsigned char *shadowTop = NULL; +conv *gaussianMap; + +static double +gaussian (double r, double x, double y) +{ + return ((1 / (sqrt (2 * M_PI * r))) * + exp ((- (x * x + y * y)) / (2 * r * r))); +} + +static conv * +make_gaussian_map (Display *dpy, double r) +{ + conv *c; + int size = ((int) ceil ((r * 3)) + 1) & ~1; + int center = size / 2; + int x, y; + double t; + double g; + + c = g_malloc (sizeof (conv) + size * size * sizeof (double)); + c->size = size; + c->data = (double *) (c + 1); + t = 0.0; + for (y = 0; y < size; y++) + for (x = 0; x < size; x++) + { + g = gaussian (r, (double) (x - center), (double) (y - center)); + t += g; + c->data[y * size + x] = g; + } +/* printf ("gaussian total %f\n", t); */ + for (y = 0; y < size; y++) + for (x = 0; x < size; x++) + { + c->data[y*size + x] /= t; + } + return c; +} + +/* + * A picture will help + * + * -center 0 width width+center + * -center +-----+-------------------+-----+ + * | | | | + * | | | | + * 0 +-----+-------------------+-----+ + * | | | | + * | | | | + * | | | | + * height +-----+-------------------+-----+ + * | | | | + * height+ | | | | + * center +-----+-------------------+-----+ + */ + +static unsigned char +sum_gaussian (conv *map, double opacity, int x, int y, int width, int height) +{ + int fx, fy; + double *g_data; + double *g_line = map->data; + int g_size = map->size; + int center = g_size / 2; + int fx_start, fx_end; + int fy_start, fy_end; + double v; + + /* + * Compute set of filter values which are "in range", + * that's the set with: + * 0 <= x + (fx-center) && x + (fx-center) < width && + * 0 <= y + (fy-center) && y + (fy-center) < height + * + * 0 <= x + (fx - center) x + fx - center < width + * center - x <= fx fx < width + center - x + */ + + fx_start = center - x; + if (fx_start < 0) + fx_start = 0; + fx_end = width + center - x; + if (fx_end > g_size) + fx_end = g_size; + + fy_start = center - y; + if (fy_start < 0) + fy_start = 0; + fy_end = height + center - y; + if (fy_end > g_size) + fy_end = g_size; + + g_line = g_line + fy_start * g_size + fx_start; + + v = 0; + for (fy = fy_start; fy < fy_end; fy++) + { + g_data = g_line; + g_line += g_size; + + for (fx = fx_start; fx < fx_end; fx++) + v += *g_data++; + } + if (v > 1) + v = 1; + + return ((unsigned char) (v * opacity * 255.0)); +} + +/* precompute shadow corners and sides to save time for large windows */ +static void +presum_gaussian (conv *map) +{ + int center = map->size/2; + int opacity, x, y; + + Gsize = map->size; + + if (shadowCorner) + g_free ((void *)shadowCorner); + if (shadowTop) + g_free ((void *)shadowTop); + + shadowCorner = (unsigned char *)(g_malloc ((Gsize + 1) * (Gsize + 1) * 26)); + shadowTop = (unsigned char *)(g_malloc ((Gsize + 1) * 26)); + + for (x = 0; x <= Gsize; x++) + { + shadowTop[25 * (Gsize + 1) + x] = sum_gaussian (map, 1, x - center, center, Gsize * 2, Gsize * 2); + for(opacity = 0; opacity < 25; opacity++) + shadowTop[opacity * (Gsize + 1) + x] = shadowTop[25 * (Gsize + 1) + x] * opacity / 25; + for(y = 0; y <= x; y++) + { + shadowCorner[25 * (Gsize + 1) * (Gsize + 1) + y * (Gsize + 1) + x] + = sum_gaussian (map, 1, x - center, y - center, Gsize * 2, Gsize * 2); + shadowCorner[25 * (Gsize + 1) * (Gsize + 1) + x * (Gsize + 1) + y] + = shadowCorner[25 * (Gsize + 1) * (Gsize + 1) + y * (Gsize + 1) + x]; + for(opacity = 0; opacity < 25; opacity++) + shadowCorner[opacity * (Gsize + 1) * (Gsize + 1) + y * (Gsize + 1) + x] + = shadowCorner[opacity * (Gsize + 1) * (Gsize + 1) + x * (Gsize + 1) + y] + = shadowCorner[25 * (Gsize + 1) * (Gsize + 1) + y * (Gsize + 1) + x] * opacity / 25; + } + } +} + +static XImage * +make_shadow (Display *dpy, double opacity, int width, int height) +{ + XImage *ximage; + unsigned char *data; + int gsize = gaussianMap->size; + int ylimit, xlimit; + int swidth = width + gsize; + int sheight = height + gsize; + int center = gsize / 2; + int x, y; + unsigned char d; + int x_diff; + int opacity_int = (int)(opacity * 25); + data = g_malloc (swidth * sheight * sizeof (unsigned char)); + if (!data) + return 0; + ximage = XCreateImage (dpy, + DefaultVisual(dpy, DefaultScreen(dpy)), + 8, + ZPixmap, + 0, + (char *) data, + swidth, sheight, 8, swidth * sizeof (unsigned char)); + if (!ximage) + { + g_free (data); + return 0; + } + /* + * Build the gaussian in sections + */ + + /* + * center (fill the complete data array) + */ + if (Gsize > 0) + d = shadowTop[opacity_int * (Gsize + 1) + Gsize]; + else + d = sum_gaussian (gaussianMap, opacity, center, center, width, height); + memset(data, d, sheight * swidth); + + /* + * corners + */ + ylimit = gsize; + if (ylimit > sheight / 2) + ylimit = (sheight + 1) / 2; + xlimit = gsize; + if (xlimit > swidth / 2) + xlimit = (swidth + 1) / 2; + + for (y = 0; y < ylimit; y++) + for (x = 0; x < xlimit; x++) + { + if (xlimit == Gsize && ylimit == Gsize) + d = shadowCorner[opacity_int * (Gsize + 1) * (Gsize + 1) + y * (Gsize + 1) + x]; + else + d = sum_gaussian (gaussianMap, opacity, x - center, y - center, width, height); + data[y * swidth + x] = d; + data[(sheight - y - 1) * swidth + x] = d; + data[(sheight - y - 1) * swidth + (swidth - x - 1)] = d; + data[y * swidth + (swidth - x - 1)] = d; + } + + /* + * top/bottom + */ + x_diff = swidth - (gsize * 2); + if (x_diff > 0 && ylimit > 0) + { + for (y = 0; y < ylimit; y++) + { + if (ylimit == Gsize) + d = shadowTop[opacity_int * (Gsize + 1) + y]; + else + d = sum_gaussian (gaussianMap, opacity, center, y - center, width, height); + memset (&data[y * swidth + gsize], d, x_diff); + memset (&data[(sheight - y - 1) * swidth + gsize], d, x_diff); + } + } + + /* + * sides + */ + + for (x = 0; x < xlimit; x++) + { + if (xlimit == Gsize) + d = shadowTop[opacity_int * (Gsize + 1) + x]; + else + d = sum_gaussian (gaussianMap, opacity, x - center, center, width, height); + for (y = gsize; y < sheight - gsize; y++) + { + data[y * swidth + x] = d; + data[y * swidth + (swidth - x - 1)] = d; + } + } + + return ximage; +} + +static Picture +shadow_picture (Display *dpy, Window root, + double opacity, int width, int height, int *wp, int *hp) +{ + XImage *shadowImage; + Pixmap shadowPixmap; + Picture shadowPicture; + GC gc; + + if (!gaussianMap) + { + gaussianMap = make_gaussian_map(dpy, SHADOW_RADIUS); + presum_gaussian (gaussianMap); + } + + shadowImage = make_shadow (dpy, opacity, width, height); + if (!shadowImage) + return None; + shadowPixmap = XCreatePixmap (dpy, root, + shadowImage->width, + shadowImage->height, + 8); + if (!shadowPixmap) + { + XDestroyImage (shadowImage); + return None; + } + + shadowPicture = XRenderCreatePicture (dpy, shadowPixmap, + XRenderFindStandardFormat (dpy, PictStandardA8), + 0, 0); + if (!shadowPicture) + { + XDestroyImage (shadowImage); + XFreePixmap (dpy, shadowPixmap); + return None; + } + + gc = XCreateGC (dpy, shadowPixmap, 0, 0); + if (!gc) + { + XDestroyImage (shadowImage); + XFreePixmap (dpy, shadowPixmap); + XRenderFreePicture (dpy, shadowPicture); + return None; + } + + XPutImage (dpy, shadowPixmap, gc, shadowImage, 0, 0, 0, 0, + shadowImage->width, + shadowImage->height); + *wp = shadowImage->width; + *hp = shadowImage->height; + XFreeGC (dpy, gc); + XDestroyImage (shadowImage); + XFreePixmap (dpy, shadowPixmap); + return shadowPicture; +} diff --git a/src/cwindow.h b/src/cwindow.h new file mode 100644 index 00000000..b2224faf --- /dev/null +++ b/src/cwindow.h @@ -0,0 +1,107 @@ +#include "config.h" +#include <X11/extensions/Xfixes.h> +#include <X11/extensions/Xdamage.h> +#include <X11/extensions/Xrender.h> +#include <glib.h> +#include "screen.h" + +#define USE_RENDER 0 + +typedef struct CWindow CWindow; +typedef struct Quad Quad; +typedef struct Point Point; +typedef struct Rectangle Rectangle; +typedef struct Distortion Distortion; + +struct Point +{ + int x, y; +}; + +struct Quad +{ + Point points[4]; +}; + +struct Rectangle +{ + int x, y; + int width, height; +}; + +struct Distortion +{ + Rectangle source; + Quad destination; +}; + +void cwindow_set_transformation (CWindow *window, + const Distortion *distortions, + int n_distortions); +void cwindow_free (CWindow *cwindow); +XserverRegion cwindow_extents (CWindow *cwindow); +XserverRegion cwindow_get_opaque_region (CWindow *cwindow); +Drawable cwindow_get_drawable (CWindow *cwindow); +Window cwindow_get_xwindow (CWindow *cwindow); +gboolean cwindow_get_viewable (CWindow *cwindow); +gboolean cwindow_get_input_only (CWindow *cwindow); +Visual * cwindow_get_visual (CWindow *cwindow); +XserverRegion cwindow_get_last_painted_extents (CWindow *cwindow); +void cwindow_set_last_painted_extents (CWindow *cwindow, + XserverRegion region); +void cwindow_destroy_last_painted_extents (CWindow *cwindow); +int cwindow_get_x (CWindow *cwindow); +int cwindow_get_y (CWindow *cwindow); +int cwindow_get_width (CWindow *cwindow); +int cwindow_get_height (CWindow *cwindow); +int cwindow_get_border_width (CWindow *cwindow); +MetaScreen * cwindow_get_screen (CWindow *cwindow); +Damage cwindow_get_damage (CWindow *cwindow); +void cwindow_set_pending_x (CWindow *cwindow, + int pending_x); +void cwindow_set_pending_y (CWindow *cwindow, + int pending_y); +void cwindow_set_pending_width (CWindow *cwindow, + int width); +void cwindow_set_pending_height (CWindow *cwindow, + int height); +void cwindow_set_pending_border_width (CWindow *cwindow, + int border_width); +void cwindow_set_x (CWindow *cwindow, + int x); +void cwindow_set_y (CWindow *cwindow, + int y); +void cwindow_set_width (CWindow *cwindow, + int width); +void cwindow_set_height (CWindow *cwindow, + int height); +Pixmap cwindow_get_pixmap (CWindow *cwindow); +void cwindow_set_border_width (CWindow *cwindow, + int border_width); +void cwindow_set_viewable (CWindow *cwindow, + gboolean viewable); +CWindow * cwindow_new (MetaCompositor *compositor, + Window xwindow, + XWindowAttributes *attrs); +XID * cwindow_get_xid_address (CWindow *cwindow); +MetaCompositor *cwindow_get_compositor (CWindow *cwindow); +#if 0 +void cwindow_draw (CWindow *cwindow, + Picture picture, + XserverRegion damaged_region); +#endif +gboolean cwindow_is_translucent (CWindow *cwindow); +void cwindow_draw_warped (CWindow *cwindow, + MetaScreen *screen, + Picture picture, + Quad *destination); +void cwindow_process_damage_notify (CWindow *cwindow, + XDamageNotifyEvent *event); +void cwindow_process_configure_notify (CWindow *cwindow, + XConfigureEvent *event); +void cwindow_draw (CWindow *cwindow, + Picture destination, + XserverRegion clip); +void cwindow_freeze (CWindow *cwindow); +void cwindow_thaw (CWindow *cwindow); +void cwindow_set_translucent (CWindow *cwindow, gboolean translucent); diff --git a/src/lmcbits.c b/src/lmcbits.c new file mode 100644 index 00000000..88fa99c3 --- /dev/null +++ b/src/lmcbits.c @@ -0,0 +1,93 @@ +#include "lmcbits.h" + +LmcBits * +lmc_bits_new (LmcBitsFormat format, + int width, + int height, + guchar *data, + int rowstride, + GDestroyNotify notify, + gpointer notify_data) +{ + LmcBits *bits = g_new0 (LmcBits, 1); + + bits->ref_count = 1; + + bits->format = format; + bits->width = width; + bits->height = height; + bits->data_ = data; + bits->rowstride = rowstride; + bits->notify = notify; + bits->notify_data = notify_data; + + return bits; +} + +LmcBits * +lmc_bits_new_from_pixbuf (GdkPixbuf *pixbuf) +{ + return lmc_bits_new (gdk_pixbuf_get_has_alpha (pixbuf) ? LMC_BITS_RGBA_MSB_32 : LMC_BITS_RGB_24, + gdk_pixbuf_get_width (pixbuf), + gdk_pixbuf_get_height (pixbuf), + gdk_pixbuf_get_pixels (pixbuf), + gdk_pixbuf_get_rowstride (pixbuf), + (GDestroyNotify)g_object_unref, + g_object_ref (pixbuf)); +} + +LmcBits * +lmc_bits_ref (LmcBits *bits) +{ + g_atomic_int_inc (&bits->ref_count); + + return bits; +} + +void +lmc_bits_unref (LmcBits *bits) +{ + if (g_atomic_int_dec_and_test (&bits->ref_count)) + { + if (bits->notify) + bits->notify (bits->notify_data); + + g_free (bits); + } +} + + +guchar * +lmc_bits_lock (LmcBits *bits) +{ + guchar *result; + + result = bits->data_; + + return result; +} + +void +lmc_bits_unlock (LmcBits *bits) +{ +} + +gboolean +lmc_bits_has_alpha (LmcBits *bits) +{ + switch (bits->format) + { + case LMC_BITS_RGB_16: + return FALSE; + case LMC_BITS_RGB_24: + return FALSE; + case LMC_BITS_RGB_32: + return FALSE; + case LMC_BITS_RGBA_MSB_32: + return TRUE; + case LMC_BITS_ARGB_32: + return TRUE; + default: + return FALSE; + } +} diff --git a/src/lmcbits.h b/src/lmcbits.h new file mode 100644 index 00000000..76b8a954 --- /dev/null +++ b/src/lmcbits.h @@ -0,0 +1,54 @@ +#ifndef __LMC_BITS_H__ +#define __LMC_BITS_H__ + +#include <glib.h> +#include <gdk-pixbuf/gdk-pixbuf.h> + +G_BEGIN_DECLS + +typedef struct _LmcBits LmcBits; + +typedef enum { + LMC_BITS_RGB_16, + LMC_BITS_RGB_24, + LMC_BITS_RGB_32, + LMC_BITS_RGBA_MSB_32, + LMC_BITS_ARGB_32 +} LmcBitsFormat; + +struct _LmcBits +{ + int ref_count; + + LmcBitsFormat format; + int width; + int height; + int rowstride; + + GDestroyNotify notify; + gpointer notify_data; + + guchar *data_; +}; + +LmcBits *lmc_bits_new (LmcBitsFormat format, + int width, + int height, + guchar *data, + int rowstride, + GDestroyNotify notify, + gpointer notify_data); + +LmcBits *lmc_bits_new_from_pixbuf (GdkPixbuf *pixbuf); + +LmcBits *lmc_bits_ref (LmcBits *bits); +void lmc_bits_unref (LmcBits *bits); + +guchar *lmc_bits_lock (LmcBits *bits); +void lmc_bits_unlock (LmcBits *bits); + +gboolean lmc_bits_has_alpha (LmcBits *bits); + +G_END_DECLS + +#endif /* __LMC_BITS_H__ */ diff --git a/src/lmctexture.c b/src/lmctexture.c new file mode 100644 index 00000000..40c75a10 --- /dev/null +++ b/src/lmctexture.c @@ -0,0 +1,805 @@ +#include "config.h" +#include "lmctexture.h" +#include <string.h> +#include <GL/glu.h> +#include "lmctypes.h" +#include "screen.h" + +typedef struct Tile Tile; + +struct Tile +{ + GdkRectangle geometry; + GLuint texture; +}; + +#define WOBBLE_TILE_SIZE 16 + +struct _LmcTexture +{ + /*< private >*/ + int ref_count; + + LmcBits *bits; + + GList *tiles; + + gint grid_width; + gint grid_height; + + LmcPoint *grid; +}; + +static int +get_max_texture_width (void) +{ + return 128; + return GL_MAX_TEXTURE_SIZE; +} + +static int +get_max_texture_height (void) +{ + return 128; + return GL_MAX_TEXTURE_SIZE; +} + +static void +dump_error (const char *context) +{ +#if 0 + /* glGetError() is a server roundtrip */ + GLenum err; + + err = glGetError (); + if (err != GL_NO_ERROR) + { + const GLubyte *message = gluErrorString (err); + g_warning ("GL Error: %s [at %s]\n", message, context); + } +#endif +} + +static int +power_of_two_less_than_or_equal (int v) +{ + int t = 1; + +#if 0 + g_print ("input: %d\n", v); +#endif + + g_return_val_if_fail (v >= 1, -1); + + while (t <= v) + t *= 2; + + t /= 2; + +#if 0 + g_print ("output: %d\n", t); +#endif + + return t; +} + +static GList * +get_tile_sizes (int width, int height) +{ + GList *l1, *l2; + GList *vertical, *horizontal, *tile_sizes; + int x, y; + int max_texture_width; + int max_texture_height; + + g_return_val_if_fail (width > 0, NULL); + g_return_val_if_fail (height > 0, NULL); + + max_texture_width = get_max_texture_width (); + max_texture_height = get_max_texture_height (); + +#if 0 + g_print ("maxw, maxh: %d %d\n", max_texture_width, + max_texture_height); +#endif + + horizontal = NULL; + while (width) + { + int t = power_of_two_less_than_or_equal (MIN (width, max_texture_width)); + + horizontal = g_list_prepend (horizontal, GINT_TO_POINTER (t)); + + width -= t; + } + + vertical = NULL; + while (height) + { + int t = power_of_two_less_than_or_equal (MIN (height, max_texture_height)); + + vertical = g_list_prepend (vertical, GINT_TO_POINTER (t)); + + height -= t; + } + + tile_sizes = NULL; + x = 0; + for (l1 = horizontal; l1 != NULL; l1 = l1->next) + { + y = 0; + for (l2 = vertical; l2 != NULL; l2 = l2->next) + { + GdkRectangle *rect = g_new0 (GdkRectangle, 1); + + rect->width = GPOINTER_TO_INT (l1->data); + rect->height = GPOINTER_TO_INT (l2->data); + rect->x = x; + rect->y = y; + + tile_sizes = g_list_prepend (tile_sizes, rect); + + y += GPOINTER_TO_INT (l2->data); + } + + x += GPOINTER_TO_INT (l1->data); + } + + g_list_free (horizontal); + g_list_free (vertical); + + return tile_sizes; +} + +static guchar * +create_buffer (LmcBits *bits, + GdkRectangle *rect) +{ + guchar *buffer = g_malloc (4 * rect->width * rect->height); + int i, j; + int bpp; + + switch (bits->format) + { + case LMC_BITS_RGB_16: + bpp = 2; + break; + case LMC_BITS_RGB_24: + bpp = 3; + break; + case LMC_BITS_RGB_32: + case LMC_BITS_RGBA_MSB_32: + case LMC_BITS_ARGB_32: + bpp = 4; + break; + default: + g_assert_not_reached (); + bpp = 4; + break; + } + + for (j = 0; j < rect->height; j++) + { + guchar *src; + guint32 *dest; + gint src_max; + + dest = (guint32 *)buffer + rect->width * j; + + if (j + rect->y >= bits->height) + { + memset (dest, 0, rect->width * 4); + continue; + } + + src = bits->data_ + bits->rowstride * (j + rect->y) + bpp * rect->x; + src_max = MIN (rect->width, bits->width - rect->x); + + for (i = 0; i < src_max; i++) + { + guchar r,g,b,a; + + switch (bits->format) + { + case LMC_BITS_RGB_16: + { + guint16 t = *(guint16 *)src; + guint tr, tg, tb; + + tr = t & 0xf800; + r = (tr >> 8) + (tr >> 13); + + tg = t & 0x07e0; + g = (tg >> 3) + (tg >> 9); + + tb = t & 0x001f; + b = (tb << 3) + (tb >> 2); + + a = 0xff; + } + break; + case LMC_BITS_RGB_24: + r = src[0]; + g = src[1]; + b = src[2]; + a = 0xff; + break; + case LMC_BITS_RGB_32: + { + guint32 t = *(guint32 *)src; + r = (t >> 16) & 0xff; + g = (t >> 8) & 0xff; + b = t & 0xff; + a = 0xff; + } + break; + case LMC_BITS_RGBA_MSB_32: + { + guint tr, tg, tb; + + a = src[3]; + tr = src[0] * a + 0x80; + r = (tr + (tr >> 8)) >> 8; + tg = src[1] * a + 0x80; + g = (tg + (tg >> 8)) >> 8; + tb = src[2] * a + 0x80; + b = (tb + (tb >> 8)) >> 8; + } + break; + case LMC_BITS_ARGB_32: + { + guint32 t = *(guint32 *)src; + r = (t >> 16) & 0xff; + g = (t >> 8) & 0xff; + b = t & 0xff; + a = t >> 24; + } + break; + default: + g_assert_not_reached(); + r = g = b = a = 0; /* Quiet GCC */ + break; + } + + *dest = (a << 24) | (r << 16) | (g << 8) | b; + + src += bpp; + dest++; + } + + for (; i < rect->width; i++) + { + *dest = 0; + dest++; + } + } + + return buffer; +} + +static GLuint +allocate_texture_name (void) +{ + GLuint name; + glGenTextures (1, &name); + return name; +} + +static Tile * +tile_new (LmcBits *bits, + GdkRectangle *tile_geometry) +{ + Tile *tile = g_new (Tile, 1); + guchar *buffer; + + tile->geometry = *tile_geometry; + tile->texture = allocate_texture_name (); + +#if 0 + g_print ("allocated %p (%d)\n", tile, tile->texture); +#endif + + buffer = create_buffer (bits, tile_geometry); + +#if 0 + g_print ("%x\n", *(int *)buffer); +#endif + + glBindTexture (GL_TEXTURE_2D, tile->texture); + + dump_error ("TexImage2D"); + + glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT); + dump_error ("TexImage2D"); + + glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT); + dump_error ("TexImage2D"); + + glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); + dump_error ("TexImage2D"); + + glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); + dump_error ("TexImage2D"); + + glPixelStorei (GL_UNPACK_ROW_LENGTH, tile->geometry.width); + dump_error ("TexImage2D"); + + glPixelStorei (GL_UNPACK_ALIGNMENT, 4); + + dump_error ("TexImage2D"); + + glTexImage2D (GL_TEXTURE_2D, 0 /* level */, GL_RGBA, + tile->geometry.width, tile->geometry.height, 0 /* border */, + GL_BGRA, GL_UNSIGNED_BYTE, buffer); + + dump_error ("TexImage2D"); + + g_free (buffer); + + return tile; +} + +static void +tile_free (Tile *tile) +{ +#if 0 + g_print ("deleting %p, %d\n", tile, tile->texture); +#endif + glDeleteTextures (1, &(tile->texture)); + + g_free (tile); +} + +static GList * +load_bits (LmcBits *bits) +{ + GList *tile_sizes = get_tile_sizes (bits->width, bits->height); + GList *list, *tiles = NULL; + + for (list = tile_sizes; list != NULL; list = list->next) + { + GdkRectangle *tile_size = list->data; + + Tile *tile = tile_new (bits, tile_size); + + tiles = g_list_prepend (tiles, tile); + } + + for (list = tile_sizes; list != NULL; list = list->next) + g_free (list->data); + g_list_free (tile_sizes); + + return tiles; +} + +LmcTexture* +lmc_texture_new (LmcBits *bits) +{ + LmcTexture *texture; + + g_return_val_if_fail (bits != NULL, NULL); + + texture = g_new0 (LmcTexture, 1); + + texture->bits = lmc_bits_ref (bits); + + texture->tiles = load_bits (bits); + + texture->ref_count = 1; + + return texture; +} + +LmcTexture* +lmc_texture_ref (LmcTexture *texture) +{ + texture->ref_count++; + return texture; +} + +void +lmc_texture_unref (LmcTexture *texture) +{ + if (--texture->ref_count == 0) + { + GList *list; + + lmc_bits_unref (texture->bits); + + for (list = texture->tiles; list != NULL; list = list->next) + tile_free (list->data); + + g_list_free (texture->tiles); + + g_free (texture); + } + else + g_print ("new value: %d\n", texture->ref_count); +} + +#if 0 +static gboolean +xrect_intersect (const XRectangle *src1, const XRectangle *src2, XRectangle *dest) +{ + gint dest_x, dest_y; + gint dest_w, dest_h; + gint return_val; + + g_return_val_if_fail (src1 != NULL, FALSE); + g_return_val_if_fail (src2 != NULL, FALSE); + g_return_val_if_fail (dest != NULL, FALSE); + + return_val = FALSE; + + dest_x = MAX (src1->x, src2->x); + dest_y = MAX (src1->y, src2->y); + dest_w = MIN (src1->x + src1->width, src2->x + src2->width) - dest_x; + dest_h = MIN (src1->y + src1->height, src2->y + src2->height) - dest_y; + + if (dest_w > 0 && dest_h > 0) + { + dest->x = dest_x; + dest->y = dest_y; + dest->width = dest_w; + dest->height = dest_h; + return_val = TRUE; + } + else + { + dest->width = 0; + dest->height = 0; + } + + return return_val; +} +#endif + +void +lmc_texture_update_rect (LmcTexture *texture, + GdkRectangle *rect) +{ + GList *list; + + for (list = texture->tiles; list != NULL; list = list->next) + { + Tile *tile = list->data; + GdkRectangle intersection; + + if (gdk_rectangle_intersect (&(tile->geometry), rect, &intersection)) + { + guchar *buffer; +#if 0 + int i; +#endif + + glBindTexture (GL_TEXTURE_2D, tile->texture); + + buffer = create_buffer (texture->bits, &intersection); + +#if 0 + for (i = 0; i < 4 * intersection.width * intersection.height; ++i) + buffer[i] = g_random_int (); +#endif + +#if 0 + g_print ("updating %d %d %d %d\n", + intersection.x, intersection.y, intersection.width, intersection.height); +#endif + + intersection.x -= tile->geometry.x; + intersection.y -= tile->geometry.y; + +#if 0 + g_print ("tile %d %d %d %d\n", + tile->geometry.x, tile->geometry.y, tile->geometry.width, tile->geometry.height); +#endif + + glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT); + dump_error ("TexImage2D"); + + glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT); + dump_error ("TexImage2D"); + + glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); + dump_error ("TexImage2D"); + + glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); + dump_error ("TexImage2D"); + + glPixelStorei (GL_UNPACK_ROW_LENGTH, intersection.width); + dump_error ("TexImage2D"); + + glPixelStorei (GL_UNPACK_ALIGNMENT, 4); + + glTexSubImage2D (GL_TEXTURE_2D, 0, + intersection.x, + intersection.y, + intersection.width, + intersection.height, + GL_BGRA, GL_UNSIGNED_BYTE, + buffer); + + dump_error ("glTexSubImage2D"); + + g_free (buffer); + } + } +} + +static void +set_clip_region (GdkRegion *region, int x, int y) +{ + + GdkRectangle *rects; + int n_rects, i; + + /* clear stencil buffer */ + glClearStencil (0); + glClear (GL_STENCIL_BUFFER_BIT); + glStencilFunc (GL_NEVER, 1, 1); + + glEnable (GL_STENCIL_TEST); + + glStencilOp (GL_REPLACE, GL_REPLACE, GL_REPLACE); + + /* draw region to stencil buffer */ + + gdk_region_offset (region, x, y); + + gdk_region_get_rectangles (region, &rects, &n_rects); + + glDisable (GL_TEXTURE_2D); + for (i = 0; i < n_rects; ++i) + { + glBegin(GL_QUADS); + + glVertex3i (rects[i].x, rects[i].y, 0); + glVertex3i (rects[i].x + rects[i].width, rects[i].y, 0); + glVertex3i (rects[i].x + rects[i].width, rects[i].y + rects[i].height, 0); + glVertex3i (rects[i].x, rects[i].y + rects[i].height, 0); + + glEnd (); + } + + g_free (rects); + + gdk_region_offset (region, -x, -y); + + glStencilFunc (GL_EQUAL, 0x1, 0x1); + glStencilOp (GL_KEEP, GL_KEEP, GL_KEEP); + + glEnable (GL_TEXTURE_2D); +} + +static void +unset_clip_region () +{ + glDisable (GL_STENCIL_TEST); +} + +static gboolean +region_intersects_rect (GdkRegion *region, GdkRectangle *rect) +{ + return (gdk_region_rect_in (region, rect) != GDK_OVERLAP_RECTANGLE_OUT); + +} + +static void +print_gdk_region (const char *name, GdkRegion *region) +{ + GdkRectangle *rects; + int i, n_rects; + + gdk_region_get_rectangles (region, &rects, &n_rects); + + g_print ("region \"%s\":\n", name); + for (i = 0; i < n_rects; ++i) + g_print (" %d %d %d %d\n", rects[i].x, rects[i].y, rects[i].width, rects[i].height); + g_free (rects); +} + +void +lmc_texture_set_deformation (LmcTexture *texture, + LmcDeformationFunc func, + void *data) +{ + int x, y, deformed_x, deformed_y; + LmcPoint *grid; + LmcBits *bits; + + if (func != NULL) + { + bits = texture->bits; + + texture->grid_width = + (bits->width + WOBBLE_TILE_SIZE - 1) / WOBBLE_TILE_SIZE + 1; + texture->grid_height = + (bits->height + WOBBLE_TILE_SIZE - 1) / WOBBLE_TILE_SIZE + 1; + grid = g_new0 (LmcPoint, texture->grid_width * texture->grid_height); + + for (y = 0; y < texture->grid_height; y++) + for (x = 0; x < texture->grid_width; x++) + { + func (x * WOBBLE_TILE_SIZE, y * WOBBLE_TILE_SIZE, + 0, 0, bits->width, bits->height, + &deformed_x, &deformed_y, + data); + grid[x + y * texture->grid_width].x = deformed_x; + grid[x + y * texture->grid_width].y = deformed_y; + } + } + else + grid = NULL; + + if (texture->grid) + g_free (texture->grid); + texture->grid = grid; +} + +static void +draw_tile (MetaScreen *screen, Tile *tile, int x, int y, GdkRegion *clip) +{ + if ((!clip || region_intersects_rect (clip, &tile->geometry))) + { + GdkRectangle *rects; + int n_rects; + int translated_x = tile->geometry.x + x; + int translated_y = tile->geometry.y + y; + int i; + GdkRectangle dummy; + + gdk_region_get_rectangles (clip, &rects, &n_rects); + + glEnable (GL_SCISSOR_TEST); + + glBindTexture (GL_TEXTURE_2D, tile->texture); + + for (i = 0; i < n_rects; ++i) + { + if (gdk_rectangle_intersect (&(rects[i]), &tile->geometry, &dummy)) + { + glScissor (rects[i].x + x, + screen->height - (y + rects[i].y + rects[i].height), + rects[i].width, + rects[i].height); + dump_error ("glbindtexture"); + + glBegin (GL_QUADS); + + /* corner 1 */ + glTexCoord2f (0.0, 0.0); + glVertex3i (translated_x, translated_y, 0); + + /* corner 2 */ + glTexCoord2f (1.0, 0.0); + glVertex3i (translated_x + tile->geometry.width, translated_y, 0); + + /* corner 3 */ + glTexCoord2f (1.0, 1.0); + glVertex3i (translated_x + tile->geometry.width, translated_y + tile->geometry.height, 0); + + /* corner 4 */ + glTexCoord2f (0.0, 1.0); + glVertex3i (translated_x, translated_y + tile->geometry.height, 0); + + glEnd (); + dump_error ("glEnd"); + } + } + } +} + +/** + * lmc_texture_draw: + * @texture: + * @alpha: + * @x: + * @y: + * @clip: + * + * Draw the texture, the clip is in window coordinates. + **/ +void +lmc_texture_draw (MetaScreen *screen, + LmcTexture *texture, + double alpha, + int x, + int y, + GdkRegion *clip) +{ + GList *list; + + glPushAttrib (GL_TEXTURE_BIT); + glEnable (GL_TEXTURE_2D); + glDisable (GL_LIGHTING); + + dump_error ("gldisable light"); + + /* Setup alhpa */ + + glTexEnvf (GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE); + glBlendFunc (GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + glEnable (GL_BLEND); + +#if 0 + glShadeModel(GL_SMOOTH); +#endif + dump_error ("glenable"); + + g_assert (glIsEnabled (GL_TEXTURE_2D)); + +#if 0 + g_print ("alpha %f\n", alpha); +#endif + +#if 0 + set_clip_region (clip, x, y); +#endif + +#if 0 + glColor4f (g_random_double(), + g_random_double(), + g_random_double(), 0.3); + + glBegin (GL_QUADS); + + glVertex3i (0, 0, 0); + glVertex3i (1600, 0, 0); + glVertex3i (1600, 1200, 0); + glVertex3i (0, 1200, 0); + + glEnd (); + + goto out; +#endif + +#if 0 + glColor4f (g_random_double(), + g_random_double(), + g_random_double(), 1); + +#endif + + + glColor4f (1.0, 1.0, 1.0, 1.0); + + /* Emit quads */ + for (list = texture->tiles; list != NULL; list = list->next) + { + Tile *tile = list->data; + + draw_tile (screen, tile, x, y, clip); + } + + glDisable (GL_TEXTURE_2D); + +#if 0 + glColor4f (0.0, 0.0, 0.0, 0.1); + for (list = texture->tiles; list != NULL; list = list->next) + { + Tile *tile = list->data; + int translated_x = tile->geometry.x + x; + int translated_y = tile->geometry.y + y; + + glBegin (GL_QUADS); +#if 0 + glColor4f (g_random_double(), + g_random_double(), + g_random_double(), 0.3); +#endif + glVertex3i (translated_x, translated_y, 0); + glVertex3i (translated_x + tile->geometry.width, translated_y, 0); + glVertex3i (translated_x + tile->geometry.width, + translated_y + tile->geometry.height, 0); + glVertex3i (translated_x, translated_y + tile->geometry.height, 0); + + glEnd (); + } +#endif + +#if 0 + unset_clip_region (); +#endif + + glPopAttrib (); +} diff --git a/src/lmctexture.h b/src/lmctexture.h new file mode 100644 index 00000000..bebe9785 --- /dev/null +++ b/src/lmctexture.h @@ -0,0 +1,36 @@ +#ifndef __LMC_TEXTURE_H__ +#define __LMC_TEXTURE_H__ + +#include "lmcbits.h" +#include "lmctypes.h" +#include <gdk/gdk.h> + +#include <X11/Xutil.h> /* For Region */ +#include <GL/gl.h> /* For GLuint */ + +#include "config.h" +#include "screen.h" + +G_BEGIN_DECLS + +typedef struct _LmcTexture LmcTexture; + +void lmc_texture_set_deformation (LmcTexture *texture, + LmcDeformationFunc func, + void *data); + +LmcTexture* lmc_texture_new (LmcBits *bits); +LmcTexture* lmc_texture_ref (LmcTexture *texture); +void lmc_texture_unref (LmcTexture *texture); +void lmc_texture_update_rect (LmcTexture *texture, + GdkRectangle *rect); +void lmc_texture_draw (MetaScreen *screen, + LmcTexture *texture, + double alpha, + int x, + int y, + GdkRegion *clip); + +G_END_DECLS + +#endif /* __LMC_TEXTURE_H__ */ diff --git a/src/lmctypes.h b/src/lmctypes.h new file mode 100644 index 00000000..45c6739e --- /dev/null +++ b/src/lmctypes.h @@ -0,0 +1,53 @@ +#ifndef __LMC_TYPES_H__ +#define __LMC_TYPES_H__ + +#include <glib.h> +#include <X11/Xlib.h> + +G_BEGIN_DECLS + +typedef struct _LmcBorderInfo LmcBorderInfo; +typedef struct _LmcPropertyValue LmcPropertyValue; +typedef struct _LmcPixel LmcPixel; +typedef struct _LmcPoint LmcPoint; + +struct _LmcBorderInfo +{ + short left, right, top, bottom; + short left_unscaled, right_unscaled, top_unscaled, bottom_unscaled; +}; + +struct _LmcPropertyValue +{ + Atom type; /* None means property does not exist */ + int format; /* 8, 16, 32 */ + + union { + char *b; + short *s; + long *l; + } data; + + unsigned long n_items; /* Number of 8, 16, or 32 bit quantities */ +}; + +struct _LmcPixel { + guchar red; + guchar green; + guchar blue; + guchar alpha; +}; + +struct _LmcPoint { + double x; + double y; +}; + +typedef void (*LmcDeformationFunc) (int u, int v, + int x, int y, int width, int height, + int *deformed_x, int *deformed_y, + void *data); + +G_END_DECLS + +#endif /* __LMC_TYPES_H__ */ diff --git a/src/matrix.c b/src/matrix.c new file mode 100644 index 00000000..55074585 --- /dev/null +++ b/src/matrix.c @@ -0,0 +1,615 @@ +/* This file is from The Gimp */ + +/* LIBGIMP - The GIMP Library + * Copyright (C) 1995-1997 Peter Mattis and Spencer Kimball + * + * matrix.c + * Copyright (C) 1998 Jay Cox <jaycox@earthlink.net> + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#include <glib.h> +#include "matrix.h" +#include <math.h> + +#define EPSILON 1e-6 + + +/** + * matrix2_identity: + * @matrix: A matrix. + * + * Sets the matrix to the identity matrix. + */ +void +matrix2_identity (Matrix2 *matrix) +{ + static const Matrix2 identity = { { { 1.0, 0.0 }, + { 0.0, 1.0 } } }; + + *matrix = identity; +} + +/** + * matrix2_mult: + * @matrix1: The first input matrix. + * @matrix2: The second input matrix which will be overwritten by the result. + * + * Multiplies two matrices and puts the result into the second one. + */ +void +matrix2_mult (const Matrix2 *matrix1, + Matrix2 *matrix2) +{ + Matrix2 tmp; + + tmp.coeff[0][0] = (matrix1->coeff[0][0] * matrix2->coeff[0][0] + + matrix1->coeff[0][1] * matrix2->coeff[1][0]); + tmp.coeff[0][1] = (matrix1->coeff[0][0] * matrix2->coeff[0][1] + + matrix1->coeff[0][1] * matrix2->coeff[1][1]); + tmp.coeff[1][0] = (matrix1->coeff[1][0] * matrix2->coeff[0][0] + + matrix1->coeff[1][1] * matrix2->coeff[1][0]); + tmp.coeff[1][1] = (matrix1->coeff[1][0] * matrix2->coeff[0][1] + + matrix1->coeff[1][1] * matrix2->coeff[1][1]); + + *matrix2 = tmp; +} + +/** + * matrix3_identity: + * @matrix: A matrix. + * + * Sets the matrix to the identity matrix. + */ +void +matrix3_identity (Matrix3 *matrix) +{ + static const Matrix3 identity = { { { 1.0, 0.0, 0.0 }, + { 0.0, 1.0, 0.0 }, + { 0.0, 0.0, 1.0 } } }; + + *matrix = identity; +} +/** + * matrix3_mult: + * @matrix1: The first input matrix. + * @matrix2: The second input matrix which will be overwritten by the result. + * + * Multiplies two matrices and puts the result into the second one. + */ +void +matrix3_mult (const Matrix3 *matrix1, + Matrix3 *matrix2) +{ + gint i, j; + Matrix3 tmp; + gdouble t1, t2, t3; + + for (i = 0; i < 3; i++) + { + t1 = matrix1->coeff[i][0]; + t2 = matrix1->coeff[i][1]; + t3 = matrix1->coeff[i][2]; + + for (j = 0; j < 3; j++) + { + tmp.coeff[i][j] = t1 * matrix2->coeff[0][j]; + tmp.coeff[i][j] += t2 * matrix2->coeff[1][j]; + tmp.coeff[i][j] += t3 * matrix2->coeff[2][j]; + } + } + + *matrix2 = tmp; +} + +/** + * matrix3_translate: + * @matrix: The matrix that is to be translated. + * @x: Translation in X direction. + * @y: Translation in Y direction. + * + * Translates the matrix by x and y. + */ +void +matrix3_translate (Matrix3 *matrix, + gdouble x, + gdouble y) +{ + gdouble g, h, i; + + g = matrix->coeff[2][0]; + h = matrix->coeff[2][1]; + i = matrix->coeff[2][2]; + + matrix->coeff[0][0] += x * g; + matrix->coeff[0][1] += x * h; + matrix->coeff[0][2] += x * i; + matrix->coeff[1][0] += y * g; + matrix->coeff[1][1] += y * h; + matrix->coeff[1][2] += y * i; +} + +void +matrix3_transpose (Matrix3 *matrix) +{ + int i, j; + for (i = 0; i < 3; ++i) + for (j = 0; j < 3; ++j) + { + int tmp; + tmp = matrix->coeff[i][j]; + matrix->coeff[i][j] = matrix->coeff[j][i]; + matrix->coeff[j][i] = tmp; + } +} + +/** + * matrix3_scale: + * @matrix: The matrix that is to be scaled. + * @x: X scale factor. + * @y: Y scale factor. + * + * Scales the matrix by x and y + */ +void +matrix3_scale (Matrix3 *matrix, + gdouble x, + gdouble y) +{ + matrix->coeff[0][0] *= x; + matrix->coeff[0][1] *= x; + matrix->coeff[0][2] *= x; + + matrix->coeff[1][0] *= y; + matrix->coeff[1][1] *= y; + matrix->coeff[1][2] *= y; +} + +/** + * matrix3_rotate: + * @matrix: The matrix that is to be rotated. + * @theta: The angle of rotation (in radians). + * + * Rotates the matrix by theta degrees. + */ +void +matrix3_rotate (Matrix3 *matrix, + gdouble theta) +{ + gdouble t1, t2; + gdouble cost, sint; + + cost = cos (theta); + sint = sin (theta); + + t1 = matrix->coeff[0][0]; + t2 = matrix->coeff[1][0]; + matrix->coeff[0][0] = cost * t1 - sint * t2; + matrix->coeff[1][0] = sint * t1 + cost * t2; + + t1 = matrix->coeff[0][1]; + t2 = matrix->coeff[1][1]; + matrix->coeff[0][1] = cost * t1 - sint * t2; + matrix->coeff[1][1] = sint * t1 + cost * t2; + + t1 = matrix->coeff[0][2]; + t2 = matrix->coeff[1][2]; + matrix->coeff[0][2] = cost * t1 - sint * t2; + matrix->coeff[1][2] = sint * t1 + cost * t2; +} + +/** + * matrix3_xshear: + * @matrix: The matrix that is to be sheared. + * @amount: X shear amount. + * + * Shears the matrix in the X direction. + */ +void +matrix3_xshear (Matrix3 *matrix, + gdouble amount) +{ + matrix->coeff[0][0] += amount * matrix->coeff[1][0]; + matrix->coeff[0][1] += amount * matrix->coeff[1][1]; + matrix->coeff[0][2] += amount * matrix->coeff[1][2]; +} + +/** + * matrix3_yshear: + * @matrix: The matrix that is to be sheared. + * @amount: Y shear amount. + * + * Shears the matrix in the Y direction. + */ +void +matrix3_yshear (Matrix3 *matrix, + gdouble amount) +{ + matrix->coeff[1][0] += amount * matrix->coeff[0][0]; + matrix->coeff[1][1] += amount * matrix->coeff[0][1]; + matrix->coeff[1][2] += amount * matrix->coeff[0][2]; +} + + +/** + * matrix3_affine: + * @matrix: The input matrix. + * @a: + * @b: + * @c: + * @d: + * @e: + * @f: + * + * Applies the affine transformation given by six values to @matrix. + * The six values form define an affine transformation matrix as + * illustrated below: + * + * ( a c e ) + * ( b d f ) + * ( 0 0 1 ) + **/ +void +matrix3_affine (Matrix3 *matrix, + gdouble a, + gdouble b, + gdouble c, + gdouble d, + gdouble e, + gdouble f) +{ + Matrix3 affine; + + affine.coeff[0][0] = a; + affine.coeff[1][0] = b; + affine.coeff[2][0] = 0.0; + + affine.coeff[0][1] = c; + affine.coeff[1][1] = d; + affine.coeff[2][1] = 0.0; + + affine.coeff[0][2] = e; + affine.coeff[1][2] = f; + affine.coeff[2][2] = 1.0; + + matrix3_mult (&affine, matrix); +} + + +/** + * matrix3_determinant: + * @matrix: The input matrix. + * + * Calculates the determinant of the given matrix. + * + * Returns: The determinant. + */ +gdouble +matrix3_determinant (const Matrix3 *matrix) +{ + gdouble determinant; + + determinant = (matrix->coeff[0][0] * + (matrix->coeff[1][1] * matrix->coeff[2][2] - + matrix->coeff[1][2] * matrix->coeff[2][1])); + determinant -= (matrix->coeff[1][0] * + (matrix->coeff[0][1] * matrix->coeff[2][2] - + matrix->coeff[0][2] * matrix->coeff[2][1])); + determinant += (matrix->coeff[2][0] * + (matrix->coeff[0][1] * matrix->coeff[1][2] - + matrix->coeff[0][2] * matrix->coeff[1][1])); + + return determinant; +} + +/** + * matrix3_invert: + * @matrix: The matrix that is to be inverted. + * + * Inverts the given matrix. + * + * Returns FALSE if the matrix is not invertible + */ +gboolean +matrix3_invert (Matrix3 *matrix) +{ + Matrix3 inv; + gdouble det; + + det = matrix3_determinant (matrix); + + if (det == 0.0) + return FALSE; + + det = 1.0 / det; + + inv.coeff[0][0] = (matrix->coeff[1][1] * matrix->coeff[2][2] - + matrix->coeff[1][2] * matrix->coeff[2][1]) * det; + + inv.coeff[1][0] = - (matrix->coeff[1][0] * matrix->coeff[2][2] - + matrix->coeff[1][2] * matrix->coeff[2][0]) * det; + + inv.coeff[2][0] = (matrix->coeff[1][0] * matrix->coeff[2][1] - + matrix->coeff[1][1] * matrix->coeff[2][0]) * det; + + inv.coeff[0][1] = - (matrix->coeff[0][1] * matrix->coeff[2][2] - + matrix->coeff[0][2] * matrix->coeff[2][1]) * det; + + inv.coeff[1][1] = (matrix->coeff[0][0] * matrix->coeff[2][2] - + matrix->coeff[0][2] * matrix->coeff[2][0]) * det; + + inv.coeff[2][1] = - (matrix->coeff[0][0] * matrix->coeff[2][1] - + matrix->coeff[0][1] * matrix->coeff[2][0]) * det; + + inv.coeff[0][2] = (matrix->coeff[0][1] * matrix->coeff[1][2] - + matrix->coeff[0][2] * matrix->coeff[1][1]) * det; + + inv.coeff[1][2] = - (matrix->coeff[0][0] * matrix->coeff[1][2] - + matrix->coeff[0][2] * matrix->coeff[1][0]) * det; + + inv.coeff[2][2] = (matrix->coeff[0][0] * matrix->coeff[1][1] - + matrix->coeff[0][1] * matrix->coeff[1][0]) * det; + + *matrix = inv; + + return TRUE; +} + + +/* functions to test for matrix properties */ + + +/** + * matrix3_is_diagonal: + * @matrix: The matrix that is to be tested. + * + * Checks if the given matrix is diagonal. + * + * Returns: TRUE if the matrix is diagonal. + */ +gboolean +matrix3_is_diagonal (const Matrix3 *matrix) +{ + gint i, j; + + for (i = 0; i < 3; i++) + { + for (j = 0; j < 3; j++) + { + if (i != j && fabs (matrix->coeff[i][j]) > EPSILON) + return FALSE; + } + } + + return TRUE; +} + +/** + * matrix3_is_identity: + * @matrix: The matrix that is to be tested. + * + * Checks if the given matrix is the identity matrix. + * + * Returns: TRUE if the matrix is the identity matrix. + */ +gboolean +matrix3_is_identity (const Matrix3 *matrix) +{ + gint i, j; + + for (i = 0; i < 3; i++) + { + for (j = 0; j < 3; j++) + { + if (i == j) + { + if (fabs (matrix->coeff[i][j] - 1.0) > EPSILON) + return FALSE; + } + else + { + if (fabs (matrix->coeff[i][j]) > EPSILON) + return FALSE; + } + } + } + + return TRUE; +} + +/* Check if we'll need to interpolate when applying this matrix. + This function returns TRUE if all entries of the upper left + 2x2 matrix are either 0 or 1 +*/ + + +/** + * matrix3_is_simple: + * @matrix: The matrix that is to be tested. + * + * Checks if we'll need to interpolate when applying this matrix as + * a transformation. + * + * Returns: TRUE if all entries of the upper left 2x2 matrix are either + * 0 or 1 + */ +gboolean +matrix3_is_simple (const Matrix3 *matrix) +{ + gdouble absm; + gint i, j; + + for (i = 0; i < 2; i++) + { + for (j = 0; j < 2; j++) + { + absm = fabs (matrix->coeff[i][j]); + if (absm > EPSILON && fabs (absm - 1.0) > EPSILON) + return FALSE; + } + } + + return TRUE; +} + +/** + * matrix4_to_deg: + * @matrix: + * @a: + * @b: + * @c: + * + * + **/ +void +matrix4_to_deg (const Matrix4 *matrix, + gdouble *a, + gdouble *b, + gdouble *c) +{ + *a = 180 * (asin (matrix->coeff[1][0]) / G_PI_2); + *b = 180 * (asin (matrix->coeff[2][0]) / G_PI_2); + *c = 180 * (asin (matrix->coeff[2][1]) / G_PI_2); +} + +void +transform_matrix_perspective (gint x1, + gint y1, + gint x2, + gint y2, + gdouble tx1, + gdouble ty1, + gdouble tx2, + gdouble ty2, + gdouble tx3, + gdouble ty3, + gdouble tx4, + gdouble ty4, + Matrix3 *result) +{ + Matrix3 matrix; + gdouble scalex; + gdouble scaley; + + scalex = scaley = 1.0; + + if ((x2 - x1) > 0) + scalex = 1.0 / (gdouble) (x2 - x1); + + if ((y2 - y1) > 0) + scaley = 1.0 / (gdouble) (y2 - y1); + + /* Determine the perspective transform that maps from + * the unit cube to the transformed coordinates + */ + { + gdouble dx1, dx2, dx3, dy1, dy2, dy3; + + dx1 = tx2 - tx4; + dx2 = tx3 - tx4; + dx3 = tx1 - tx2 + tx4 - tx3; + + dy1 = ty2 - ty4; + dy2 = ty3 - ty4; + dy3 = ty1 - ty2 + ty4 - ty3; + + /* Is the mapping affine? */ + if ((dx3 == 0.0) && (dy3 == 0.0)) + { + matrix.coeff[0][0] = tx2 - tx1; + matrix.coeff[0][1] = tx4 - tx2; + matrix.coeff[0][2] = tx1; + matrix.coeff[1][0] = ty2 - ty1; + matrix.coeff[1][1] = ty4 - ty2; + matrix.coeff[1][2] = ty1; + matrix.coeff[2][0] = 0.0; + matrix.coeff[2][1] = 0.0; + } + else + { + gdouble det1, det2; + + det1 = dx3 * dy2 - dy3 * dx2; + det2 = dx1 * dy2 - dy1 * dx2; + + if (det1 == 0.0 && det2 == 0.0) + matrix.coeff[2][0] = 1.0; + else + matrix.coeff[2][0] = det1 / det2; + + det1 = dx1 * dy3 - dy1 * dx3; + + if (det1 == 0.0 && det2 == 0.0) + matrix.coeff[2][1] = 1.0; + else + matrix.coeff[2][1] = det1 / det2; + + matrix.coeff[0][0] = tx2 - tx1 + matrix.coeff[2][0] * tx2; + matrix.coeff[0][1] = tx3 - tx1 + matrix.coeff[2][1] * tx3; + matrix.coeff[0][2] = tx1; + + matrix.coeff[1][0] = ty2 - ty1 + matrix.coeff[2][0] * ty2; + matrix.coeff[1][1] = ty3 - ty1 + matrix.coeff[2][1] * ty3; + matrix.coeff[1][2] = ty1; + } + + matrix.coeff[2][2] = 1.0; + } + + matrix3_identity (result); + matrix3_translate (result, -x1, -y1); + matrix3_scale (result, scalex, scaley); + matrix3_mult (&matrix, result); +} + +void +matrix3_multiply_vector (const Matrix3 *A, + const Vector3 *v, + Vector3 *result) +{ + result->coeff[0] = + v->coeff[0] * A->coeff[0][0] + + v->coeff[1] * A->coeff[1][0] + + v->coeff[2] * A->coeff[2][0]; + result->coeff[1] = + v->coeff[0] * A->coeff[0][1] + + v->coeff[1] * A->coeff[1][1] + + v->coeff[2] * A->coeff[2][1]; + result->coeff[2] = + v->coeff[0] * A->coeff[0][2] + + v->coeff[1] * A->coeff[1][2] + + v->coeff[2] * A->coeff[2][2]; +} + +#if 0 +void +solve_3x3 (const Matrix3 *A, + Vector3 *x, + const Vector3 *b) +{ + Matrix3 inv = *A; + if (matrix3_invert (&inv)) + { + matrix3_multiply_vector (&inv, b, x); + return TRUE; + } + else + { + return FALSE; + } +} +#endif diff --git a/src/matrix.h b/src/matrix.h new file mode 100644 index 00000000..1bc6cba9 --- /dev/null +++ b/src/matrix.h @@ -0,0 +1,118 @@ +/* LIBGIMP - The GIMP Library + * Copyright (C) 1995-1997 Peter Mattis and Spencer Kimball + * + * matrix.h + * Copyright (C) 1998 Jay Cox <jaycox@earthlink.net> + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#ifndef __MATRIX_H__ +#define __MATRIX_H__ + +G_BEGIN_DECLS + +/* For information look into the C source or the html documentation */ + +typedef struct _Matrix2 Matrix2; +typedef struct _Matrix3 Matrix3; +typedef struct _Matrix4 Matrix4; +typedef struct _Vector3 Vector3; + +struct _Matrix2 +{ + gdouble coeff[2][2]; +}; + +struct _Matrix3 +{ + gdouble coeff[3][3]; +}; + +struct _Matrix4 +{ + gdouble coeff[4][4]; +}; + +struct _Vector3 +{ + gdouble coeff[3]; +}; + + +void matrix2_identity (Matrix2 *matrix); +void matrix2_mult (const Matrix2 *matrix1, + Matrix2 *matrix2); + +void matrix3_transpose (Matrix3 *matrix); + +void matrix3_identity (Matrix3 *matrix); +void matrix3_mult (const Matrix3 *matrix1, + Matrix3 *matrix2); +void matrix3_translate (Matrix3 *matrix, + gdouble x, + gdouble y); +void matrix3_scale (Matrix3 *matrix, + gdouble x, + gdouble y); +void matrix3_rotate (Matrix3 *matrix, + gdouble theta); +void matrix3_xshear (Matrix3 *matrix, + gdouble amount); +void matrix3_yshear (Matrix3 *matrix, + gdouble amount); +void matrix3_affine (Matrix3 *matrix, + gdouble a, + gdouble b, + gdouble c, + gdouble d, + gdouble e, + gdouble f); +gdouble matrix3_determinant (const Matrix3 *matrix); +gboolean matrix3_invert (Matrix3 *matrix); +gboolean matrix3_is_diagonal (const Matrix3 *matrix); +gboolean matrix3_is_identity (const Matrix3 *matrix); +gboolean matrix3_is_simple (const Matrix3 *matrix); +void matrix3_multiply_vector (const Matrix3 *A, + const Vector3 *x, + Vector3 *result); +void matrix3_transform_point (const Matrix3 *matrix, + gdouble x, + gdouble y, + gdouble *newx, + gdouble *newy); + +void matrix4_to_deg (const Matrix4 *matrix, + gdouble *a, + gdouble *b, + gdouble *c); +void transform_matrix_perspective (gint x1, + gint y1, + gint x2, + gint y2, + gdouble tx1, + gdouble ty1, + gdouble tx2, + gdouble ty2, + gdouble tx3, + gdouble ty3, + gdouble tx4, + gdouble ty4, + Matrix3 *result); + +G_END_DECLS + +#endif /* __MATRIX_H__ */ diff --git a/src/snow.c b/src/snow.c new file mode 100644 index 00000000..5aa05fbf --- /dev/null +++ b/src/snow.c @@ -0,0 +1,380 @@ +#include <X11/Xlib.h> +#include <glib.h> +#include <X11/extensions/Xfixes.h> +#include "config.h" +#include "snow.h" +#include "screen.h" +#include <math.h> + +typedef struct Flake Flake; + +#define MAX_RADIUS 6.2 +#define MIN_RADIUS 4.5 +#define MIN_ALPHA 20.0 +#define MAX_ALPHA 70.0 + +struct Flake +{ + World * world; + double x; + double y; + int alpha; + int radius; + double y_speed; + double increment; + double angle; + XRenderColor color; +}; + +struct World +{ + Display *dpy; + MetaScreen *screen; + GList *flakes; + gdouble time; + gint xmouse; +}; + +static void +flake_renew (Flake *flake, gboolean first) +{ + flake->x = g_random_double () * flake->world->screen->width; + + if (first) + flake->y = g_random_double () * flake->world->screen->height; + else + flake->y = 0.0; + + flake->y_speed = (g_random_double () * 1.8 + 0.2) * flake->world->screen->height; + flake->alpha = g_random_double_range (MIN_ALPHA, MAX_ALPHA); + flake->radius = MIN_RADIUS + ((flake->alpha - MIN_ALPHA) / (MAX_ALPHA - MIN_ALPHA)) * (MAX_RADIUS - MIN_RADIUS); + flake->increment = -0.025 + g_random_double() * 0.05; + flake->angle = 0.0; + +#define BORING + +#define GRAYNESS 0xffff + +#ifdef BORING + flake->color.red = (flake->alpha / 100.0) * GRAYNESS; + flake->color.green = (flake->alpha / 100.0) * GRAYNESS; + flake->color.blue = (flake->alpha / 100.0) * GRAYNESS; + flake->color.alpha = (flake->alpha / 100.0) * 0xffff; +#else + flake->color.red = (flake->alpha / 100.0) * g_random_int_range (2 << 14, GRAYNESS); + flake->color.green = (flake->alpha / 100.0) * 0xEE00; + flake->color.blue = (flake->alpha / 100.0) * GRAYNESS; + flake->color.alpha = (flake->alpha / 100.0) * 0xFFFF; +#endif +} + +static Flake * +flake_new (World *world, + gdouble time, + int radius) +{ + Flake *flake = g_new (Flake, 1); + + flake->world = world; + + flake_renew (flake, TRUE); + + return flake; +} + +static void +flake_move (Flake *flake, + gdouble delta) +{ + gboolean mouse_enabled = TRUE; + + flake->angle += delta * flake->increment; + + if (mouse_enabled) + { + flake->x += (flake->world->screen->width / 2 - flake->world->xmouse) / 100; + flake->y += delta * 10 * flake->y_speed; + + if ((flake->y > flake->world->screen->height || flake->y < 0)) + { + flake_renew (flake, FALSE); + } + + while (flake->x < 0) + flake->x += flake->world->screen->width; + } + else + { + flake->x += flake->radius * 25000 * delta * sin (flake->angle); + flake->y += delta * flake->y_speed; + + if ((flake->x > flake->world->screen->width || flake->x < 0) || + (flake->y > flake->world->screen->height || flake->y < 0)) + { + flake_renew (flake, FALSE); + } + } +} + +static void +flake_get_position (Flake *flake, + int *x, + int *y) +{ + if (x) + { + *x = flake->x; + *x = *x % flake->world->screen->width; + } + + if (y) + { + *y = flake->y; + *y = *y % flake->world->screen->height; + } +} + +static void +flake_get_rectangle (Flake *flake, double time, XRectangle *rect) +{ + int x, y; + + flake_get_position (flake, &x, &y); + + rect->x = x - flake->radius; + rect->y = y - flake->radius; + rect->width = 2 * flake->radius; + rect->height = 2 * flake->radius; +} + +static void +flake_destroy (Flake *flake) +{ + g_free (flake); +} + +static void +flake_invalidate (Flake *flake, + gdouble time, + XserverRegion region) +{ + XRectangle rect; + XserverRegion flake_region; + + g_return_if_fail (region != None); + + flake_get_rectangle (flake, time, &rect); + + flake_region = XFixesCreateRegion (flake->world->dpy, &rect, 1); + + XFixesUnionRegion (flake->world->dpy, region, region, flake_region); + + XFixesDestroyRegion (flake->world->dpy, flake_region); +} + +void +XRenderCompositeTrapezoids (Display *dpy, + int op, + Picture src, + Picture dst, + _Xconst XRenderPictFormat *maskFormat, + int xSrc, + int ySrc, + _Xconst XTrapezoid *traps, + int ntrap); + +static double +integral (int r, int x) +{ + g_return_val_if_fail (x <= r, -1.0); + + if (x == r) + return 0.25 * M_PI * r * r; + + return 0.5 * (x * sqrt (r * r - x * x) + r * r * atan ( x / sqrt (r * r - x * x))); +} + +static void +fill_circle (Display *dpy, Picture destination, int x, int y, int radius, XRenderColor *color) +{ + int i; + for (i = 0; i < radius; ++i) + { + XRenderColor antialias; + + double value = integral (radius, i + 1) - integral (radius, i); + int intpart = value; + double fract = value - intpart; + + static int j; + +#if 0 + if (j++ % 5000 == 0) + g_print ("%d (r: %d, i: %d)\n", intpart, radius, i); +#endif + + XRenderFillRectangle (dpy, PictOpOver, destination, color, + i + x, y - intpart, + 1, 2 * intpart); + XRenderFillRectangle (dpy, PictOpOver, destination, color, + x - i - 1, y - intpart, + 1, 2 * intpart); + + antialias = *color; + antialias.red *= fract; + antialias.green *= fract; + antialias.blue *= fract; + antialias.alpha *= fract; + + XRenderFillRectangle (dpy, PictOpOver, destination, &antialias, + i + x, y - intpart - 1, + 1, 1); + XRenderFillRectangle (dpy, PictOpOver, destination, &antialias, + i + x, y + intpart, + 1, 1); + XRenderFillRectangle (dpy, PictOpOver, destination, &antialias, + x - i - 1, y - intpart - 1, + 1, 1); + XRenderFillRectangle (dpy, PictOpOver, destination, &antialias, + x - i - 1, y + intpart, + 1, 1); + } +} + +static void +flake_paint (Flake *flake, Picture destination) +{ + int x, y; + + flake_get_position (flake, &x, &y); + + double radius = flake->radius; + +#if 0 + g_print (" %d %d %d %d \n", color.red, color.green, color.blue, color.alpha); +#endif + + fill_circle (flake->world->dpy, destination, x, y, radius, &flake->color); + +#if 0 + XRenderFillRectangle (flake->world->dpy, PictOpOver, destination, &flake->color, + x - radius, y - radius, + 2 * radius, 2 * radius); +#endif + + radius = 0.63 * radius; + + fill_circle (flake->world->dpy, destination, x, y, radius, &flake->color); + +#if 0 + XRenderFillRectangle (flake->world->dpy, PictOpOver, destination, &flake->color, + x - radius, y - radius, + 2 * radius, 2 * radius); +#endif +} + +/* + * World + */ + +World * +world_new (Display *dpy, + MetaScreen *screen) +{ +#define N_FLAKES (screen->width / 20) +#if 0 +#define N_FLAKES 10 +#endif + int i; + World *world = g_new (World, 1); + + world->dpy = dpy; + world->flakes = NULL; + world->screen = screen; + + for (i = 0; i < N_FLAKES; ++i) + { + Flake *flake = flake_new (world, 0.0, 2); + + world->flakes = g_list_prepend (world->flakes, flake); + } + + return world; +} + +void +world_set_time (World *world, double time) +{ + GList *list; + gdouble delta = time - world->time; + world->time = time; + Window dummy; + int dummyint; + + XQueryPointer (world->dpy, + world->screen->xroot, + &dummy, /* root_return */ + &dummy, /* child return */ + &world->xmouse, /* root x return */ + &dummyint, /* root y return */ + &dummyint, /* win x return */ + &dummyint, /* win y return */ + &dummyint /* mask return */); + + for (list = world->flakes; list; list = list->next) + { + Flake *flake = list->data; + + flake_move (flake, delta); + } +} + +#if 0 +void +world_start (World *world, gdouble time) +{ + g_timeout_add (25, update_world, world); +} +#endif + +MetaScreen * +world_get_screen (World *world) +{ + return world->screen; +} + +XserverRegion +world_invalidate (World *world) +{ + GList *list; + + XserverRegion region; + + region = XFixesCreateRegion (world->dpy, NULL, 0); + + for (list = world->flakes; list != NULL; list = list->next) + { + Flake *flake = list->data; + + flake_invalidate (flake, world->time, region); + } + + return region; +} + +void +world_paint (World *world, Picture destination) +{ + GList *list; + + list = world->flakes; + while (list) + { + GList *next = list->next; + Flake *flake = list->data; + + flake_paint (flake, destination); + + list = next; + } +} diff --git a/src/snow.h b/src/snow.h new file mode 100644 index 00000000..2e294df3 --- /dev/null +++ b/src/snow.h @@ -0,0 +1,16 @@ +#include <X11/extensions/Xfixes.h> +#include <X11/extensions/Xrender.h> +#include "display.h" + +typedef struct World World; + +World * +world_new (Display *display, MetaScreen *screen); +XserverRegion +world_invalidate (World *world); +void +world_set_time (World *world, gdouble time); +void +world_paint (World *world, Picture destination); +MetaScreen * +world_get_screen (World *world); |