/* $Id: xshmmap.c,v 1.10 2003/12/13 16:35:16 j_ali Exp $ */ /* GTK+ NetHack Copyright (c) Issei Numata 1999-2000 Copyright (c) Slash'EM Development Team 2000-2003 GTK+ NetHack may be freely redistributed. See license for details. */ #include #include #include #include "winGTK.h" /* * [ALI] This module deals with displaying the map on the screen. * * API: * * xshm_map_init(mode, width, height) * Initialize the map. This function should also be called if the * size needs to be changed (ie., when changing tilesets). It is * better not to call xshm_map_destroy() first. Mode should be * set to XSHM_MAP_IMAGE if the data to be sent to the map requires * uploading raster data to the server and XSHM_MAP_PIXMAP if the * data will either already be present on the server or if it can * be created with back-end rendering calls. In the first case * xshm_map_image will be initialised and xshm_map_pixmap will be * NULL, in the second case the reverse applies. * Note: XSHM_MAP_PIXBUF is supported (but unused by default). * It appears to have no advantages over XSHM_MAP_IMAGE. * xshm_map_destroy() * Free all resources allocated by xshm_map_init(). * xshm_map_get_hadjustment() and xshm_map_get_vadjustment() * Get the current scrolling adjustment controls. * xshm_map_cliparound(x, y) * Arrange for xshm_map_flush() to scroll the map so that it is centered * on pixel (x, y). * xshm_map_set_button_handler(func, data) * Register a function to be called when a button press event is * received on the map. * * Drawing: * * xshm_map_draw(area) * Mark the given area in map as needing to be propagated to the screen. * xshm_map_flush() * Action the draw queue and cliparound requested. * * It is perfectly permissible to draw directly to xshm_map_pixbuf, * xshm_map_image or xshm_map_pixmap as appropriate and then call * xshm_map_draw() to request the areas to be updated. The following * functions are provided for convenience: * * xshm_map_clear() * Clear the map to zero. * xshm_map_draw_image(src, src_x, src_y, dest_x, dest_y, width, height) * Copy the src image to the map. The image must be of the same * format as the map. This can be guaranteed by using gdk_image_new() * and using the visual of a realised window on the same display. * Supported modes: image and pixmap. * xshm_map_draw_drawable(src, src_x, src_y, dest_x, dest_y, width, height) * Copy the src drawable to the map. * Supported modes: image and pixmap. * xshm_map_draw_pixbuf(src, src_x, src_y, dest_x, dest_y, width, height) * Copy the src pixbuf to the map. * Supported modes: pixbuf only. */ GdkImage *xshm_map_image; GdkPixmap *xshm_map_pixmap; GdkPixbuf *xshm_map_pixbuf; static struct xshm_private { GdkPixmap *pixmap; /* Backing store if needed */ GtkWidget *area; /* The on-screen window */ GdkRegion *dirty; /* The draw list */ GdkGC *gc; /* gc used to draw to xshm_map_pixmap */ enum xshm_map_mode mode; int map_width, map_height; GtkAdjustment *hadj, *vadj; GtkWidget *hscrollbar; GtkWidget *vscrollbar; GtkWidget *vbox, *hbox; int areax, areay; /* The current offset of area within map */ /* Handler for button press events */ void (*button_press)(void *, GdkEventButton *, gpointer); gpointer button_press_data; int is_pending; /* True if flush() needed to update map */ } xshm; #ifdef DEBUG #include #include static void time_stamp(FILE *fp) { struct timeval tv; gettimeofday(&tv,NULL); fprintf(fp, "%d.%06d: ",tv.tv_sec,tv.tv_usec); } #endif GtkAdjustment * xshm_map_get_hadjustment(void) { return xshm.hadj; } GtkAdjustment * xshm_map_get_vadjustment(void) { return xshm.vadj; } /* * This routine maintains the dirty region which is the set of rectangles * which have been modified in the map since the last xshm_map_flush(). * If we are in non-shared image or pixbuf mode then this region needs * copying to the backing store. In all cases, this region needs copying * from the backing store to the screen. */ void xshm_map_draw(GdkRectangle *rect) { if (xshm.dirty) gdk_region_union_with_rect(xshm.dirty, rect); else xshm.dirty = gdk_region_rectangle(rect); xshm.is_pending = TRUE; } /* * This routine is responsible for arranging for the expose handler * to be called and for providing it with the correct area of the * screen to be exposed. This will be the whole of the visble map * if scrolling has taken place, or the visible part of the dirty * list otherwise. * * Note: The expose handler is responsible for clearing the dirty * list in due course. */ void xshm_map_flush(void) { int i; GdkRectangle clip, expose_area; g_return_if_fail(xshm.area != NULL); xshm.is_pending = FALSE; if (xshm.areax != (int)xshm.hadj->value || xshm.areay != (int)xshm.vadj->value) { clip.x = clip.y = 0; clip.width = xshm.hadj->page_size; clip.height = xshm.vadj->page_size; #ifdef DEBUG time_stamp(stderr); fprintf(stderr, "Map: flush at (%d, %d) size %d x %d\n", (int)xshm.hadj->value, (int)xshm.vadj->value, clip.width, clip.height); #endif gtk_widget_draw(xshm.area, &clip); } else if (xshm.dirty) { gdk_region_get_clipbox(xshm.dirty, &expose_area); #ifdef DEBUG time_stamp(stderr); fprintf(stderr, "Map: flush at (%d, %d) size %d x %d\n", expose_area.x, expose_area.y, expose_area.width, expose_area.height); #endif expose_area.x -= xshm.hadj->value; expose_area.y -= xshm.vadj->value; clip.x = clip.y = 0; clip.width = xshm.hadj->page_size; clip.height = xshm.vadj->page_size; if (gdk_rectangle_intersect(&expose_area, &clip, &expose_area)) gtk_widget_draw(xshm.area, &expose_area); } } /* * Adjust clipping so that pixel (x, y) on the map is approximately * in the centre of the drawing area. */ void xshm_map_cliparound(int x, int y) { int vx, vy; #ifdef DEBUG time_stamp(stderr); fprintf(stderr, "Map: Cliparound (%d, %d)\n", x, y); #endif vx = x - xshm.hadj->page_size / 2; vy = y - xshm.vadj->page_size / 2; if (vx < xshm.hadj->lower) vx = xshm.hadj->lower; else if (vx > xshm.hadj->upper - xshm.hadj->page_size) vx = xshm.hadj->upper - xshm.hadj->page_size; if (vy < xshm.vadj->lower) vy = xshm.vadj->lower; else if (vy > xshm.vadj->upper - xshm.vadj->page_size) vy = xshm.vadj->upper - xshm.vadj->page_size; /* Must be done before changing the adjustments so that scroll_event() * knows that a xshm_map_flush() is pending. */ xshm.is_pending = TRUE; if (vx != xshm.hadj->value) gtk_adjustment_set_value(xshm.hadj, vx); if (vy != xshm.vadj->value) gtk_adjustment_set_value(xshm.vadj, vy); } /* * Re-configure the drawing area. If the drawing area is bigger than the * map (ie., the user has stretched it), the area beyond the map will * just be background. */ static int configure_event(GtkWidget *widget, GdkEventConfigure *event) { #ifdef DEBUG fprintf(stderr, "Area: Configure (%d x %d)\n", event->width, event->height); #endif xshm.hadj->page_size = (gfloat)event->width; xshm.hadj->page_increment = (gfloat)event->width; if (xshm.hadj->value < xshm.hadj->lower) gtk_adjustment_set_value(xshm.hadj, xshm.hadj->lower); else if (xshm.hadj->value > xshm.hadj->upper - xshm.hadj->page_size) gtk_adjustment_set_value(xshm.hadj, xshm.hadj->upper - xshm.hadj->page_size); gtk_signal_emit_by_name(GTK_OBJECT(xshm.hadj), "changed"); xshm.vadj->page_size = (gfloat)event->height; xshm.vadj->page_increment = (gfloat)event->height; if (xshm.vadj->value < xshm.vadj->lower) gtk_adjustment_set_value(xshm.vadj, xshm.vadj->lower); else if (xshm.vadj->value > xshm.vadj->upper - xshm.vadj->page_size) gtk_adjustment_set_value(xshm.vadj, xshm.vadj->upper - xshm.vadj->page_size); gtk_signal_emit_by_name(GTK_OBJECT(xshm.vadj), "changed"); } /* * This function serves three purposes: * - It updates the backing store from the master copy. * (only needed in non-shared image and pibxuf modes) * - It copies the dirty region of the backing store to the window. * - It refreshes the exposed area of the window from backing store. * * By doing all these at the same time, we can reduce problems with jerky * motion caused by either updating the map and then scrolling it or by * scrolling it and then updating (neither of which look good). * * In order to maintain this, callers shouldn't flush the map until all * updating and scrolling has been completed. */ static gint expose_event(GtkWidget *widget, GdkEventExpose *event) { GdkGC *gc; int x = (int)xshm.hadj->value; int y = (int)xshm.vadj->value; #ifdef DEBUG time_stamp(stderr); fprintf(stderr, "Map: expose_event at (%d, %d) size %d x %d ... ", event->area.x, event->area.y, event->area.width, event->area.height); #endif gc = gdk_gc_new(widget->window); /* First update the backing store if that needs doing */ if (xshm.pixmap && xshm.dirty && (xshm_map_image || xshm_map_pixbuf)) { /* TODO: Should restrict this to the visible portion */ gdk_gc_set_clip_region(gc, xshm.dirty); if (xshm_map_image) gdk_draw_image(xshm.pixmap, gc, xshm_map_image, 0, 0, 0, 0, xshm.map_width, xshm.map_height); else /* if (xshm_map_pixbuf) */ gdk_pixbuf_render_to_drawable(xshm_map_pixbuf, xshm.pixmap, gc, 0, 0, 0, 0, xshm.map_width, xshm.map_height, GDK_RGB_DITHER_NORMAL, 0, 0); gdk_gc_set_clip_region(gc, NULL); #ifdef DEBUG fprintf(stderr, "backing pixmap updated ... "); #endif } /* Now update the screen from the backing store */ if (xshm.pixmap) gdk_draw_pixmap(widget->window, gc, xshm.pixmap, x + event->area.x, y + event->area.y, event->area.x, event->area.y, min(event->area.width, xshm.map_width), min(event->area.height, xshm.map_height)); else gdk_draw_image(widget->window, gc, xshm_map_image, x + event->area.x, y + event->area.y, event->area.x, event->area.y, min(event->area.width, xshm.map_width), min(event->area.height, xshm.map_height)); if (event->area.width > xshm.map_width || event->area.height > xshm.map_height) { gdk_gc_set_foreground(gc, &widget->style->bg[GTK_STATE_NORMAL]); if (event->area.width > xshm.map_width) gdk_draw_rectangle(widget->window, gc, TRUE, xshm.map_width, 0, event->area.width - xshm.map_width, xshm.map_height); if (event->area.height > xshm.map_height) gdk_draw_rectangle(widget->window, gc, TRUE, 0, xshm.map_height, event->area.width, event->area.height - xshm.map_height); } xshm.areax = x; xshm.areay = y; #ifdef DEBUG fprintf(stderr, "screen updated\n"); #endif /* And clean up */ if (xshm.dirty) { gdk_region_destroy(xshm.dirty); xshm.dirty = NULL; } gdk_gc_unref(gc); return TRUE; } static void scroll_event(GtkAdjustment *adj, gpointer data) { GdkRectangle view; g_return_if_fail(adj == xshm.hadj || adj == xshm.vadj); #ifdef DEBUG time_stamp(stderr); #endif if (!xshm.is_pending) { #ifdef DEBUG if (adj == xshm.hadj) fprintf(stderr, "Horizontal scroll event\n"); else fprintf(stderr, "Vertical scroll event\n"); #endif view.x = view.y = 0; view.width = xshm.hadj->page_size; view.height = xshm.vadj->page_size; gtk_widget_draw(xshm.area, &view); } #ifdef DEBUG else fprintf(stderr, "Clipping scroll event\n"); #endif } static int xshm_map_size(int mode, int width, int height) { GdkGC *gc; GdkVisual *visual; GdkRectangle rect; int i, j; xshm.hadj->upper = width - 1; if (xshm.hadj->value < xshm.hadj->lower) gtk_adjustment_set_value(xshm.hadj, xshm.hadj->lower); else if (xshm.hadj->value > xshm.hadj->upper - xshm.hadj->page_size) gtk_adjustment_set_value(xshm.hadj, xshm.hadj->upper - xshm.hadj->page_size); gtk_signal_emit_by_name(GTK_OBJECT(xshm.hadj), "changed"); xshm.vadj->upper = height - 1; if (xshm.vadj->value < xshm.vadj->lower) gtk_adjustment_set_value(xshm.vadj, xshm.vadj->lower); else if (xshm.vadj->value > xshm.vadj->upper - xshm.vadj->page_size) gtk_adjustment_set_value(xshm.vadj, xshm.vadj->upper - xshm.vadj->page_size); gtk_signal_emit_by_name(GTK_OBJECT(xshm.vadj), "changed"); if (width == xshm.map_width && height == xshm.map_height && (mode == XSHM_MAP_IMAGE ? xshm_map_image != 0 : mode == XSHM_MAP_PIXMAP ? xshm_map_pixmap != 0 : xshm_map_pixbuf != 0)) return 0; /* Map configuration unchanged */ #ifdef DEBUG time_stamp(stderr); fprintf(stderr, "Map: Resize (%d x %d)\n", width, height); #endif if (xshm.dirty) { gdk_region_destroy(xshm.dirty); xshm.dirty = NULL; } if (xshm.gc) { gdk_gc_unref(xshm.gc); xshm.gc = NULL; } if (xshm_map_pixmap) { gdk_pixmap_unref(xshm_map_pixmap); xshm_map_pixmap = NULL; xshm.pixmap = NULL; } else if (xshm_map_image) { gdk_image_destroy(xshm_map_image); xshm_map_image = NULL; if (xshm.pixmap) { gdk_pixmap_unref(xshm.pixmap); xshm.pixmap = NULL; } } else if (xshm_map_pixbuf) { gdk_pixbuf_unref(xshm_map_pixbuf); xshm_map_pixbuf = NULL; gdk_pixmap_unref(xshm.pixmap); xshm.pixmap = NULL; } if (mode == XSHM_MAP_PIXBUF) { #ifdef DEBUG fprintf(stderr, "Map: Switching to pixbuf mode\n"); #endif /* We don't really want an alpha channel (it doesn't have * any meaning) but GDK doesn't support alpha -> non-alpha. */ xshm_map_pixbuf = gdk_pixbuf_new(GDK_COLORSPACE_RGB, TRUE, 8, width, height); memset(gdk_pixbuf_get_pixels(xshm_map_pixbuf), 0, height * gdk_pixbuf_get_rowstride(xshm_map_pixbuf)); xshm.pixmap = gdk_pixmap_new(xshm.area->window, width, height, -1); /* Pixmap needs updating; mark it as such */ rect.x = rect.y = 0; rect.width = max(width, xshm.map_width); rect.height = max(height, xshm.map_height); xshm.dirty = gdk_region_rectangle(&rect); } else if (mode == XSHM_MAP_IMAGE) { #ifdef DEBUG fprintf(stderr, "Map: Switching to image mode\n"); #endif visual = gdk_drawable_get_visual(xshm.area->window); xshm_map_image = gdk_image_new(GDK_IMAGE_SHARED, visual, width, height); if (!xshm_map_image) { xshm_map_image = gdk_image_new(GDK_IMAGE_NORMAL, visual, width, height); xshm.pixmap = gdk_pixmap_new(xshm.area->window, width, height, -1); } else xshm.pixmap = NULL; memset(xshm_map_image->mem, 0, height * xshm_map_image->bpl); if (xshm.pixmap) { /* Pixmap also needs updating; mark it as such */ rect.x = rect.y = 0; rect.width = max(width, xshm.map_width); rect.height = max(height, xshm.map_height); xshm.dirty = gdk_region_rectangle(&rect); } else xshm.dirty = NULL; } else { #ifdef DEBUG fprintf(stderr, "Map: Switching to pixmap mode\n"); #endif xshm.pixmap = gdk_pixmap_new(xshm.area->window, width, height, -1); xshm.gc = gdk_gc_new(xshm.pixmap); gdk_gc_set_foreground(xshm.gc, &xshm.area->style->bg[GTK_STATE_NORMAL]); gdk_draw_rectangle(xshm.pixmap, xshm.gc, TRUE, 0, 0, width, height); xshm_map_pixmap = xshm.pixmap; /* Area outside new map needs updating; mark it as such */ if (width < xshm.map_width) { rect.x = width; rect.y = 0; rect.width = xshm.map_width - width; rect.height = xshm.map_height; xshm.dirty = gdk_region_rectangle(&rect); } else xshm.dirty = NULL; if (height < xshm.map_height) { rect.x = 0; rect.y = height; rect.width = xshm.map_width; rect.height = xshm.map_height - height; xshm.dirty = gdk_region_rectangle(&rect); if (xshm.dirty) gdk_region_union_with_rect(xshm.dirty, &rect); else xshm.dirty = gdk_region_rectangle(&rect); } } xshm.map_width = width; xshm.map_height = height; #ifdef DEBUG fprintf(stderr, "New configuration:\n"); if (xshm_map_image) fprintf(stderr, "\txshm_map_image: %d x %d\n", xshm_map_image->width, xshm_map_image->height); else fprintf(stderr, "\txshm_map_image: none\n"); if (xshm_map_pixmap) { gdk_drawable_get_size(xshm_map_pixmap, &i, &j); fprintf(stderr, "\txshm_map_pixmap: %d x %d\n", i, j); } else fprintf(stderr, "\txshm_map_pixmap: none\n"); if (xshm_map_pixbuf) fprintf(stderr, "\txshm_map_pixbuf: %d x %d\n", gdk_pixbuf_get_width(xshm_map_pixbuf), gdk_pixbuf_get_height(xshm_map_pixbuf)); else fprintf(stderr, "\txshm_map_pixbuf: none\n"); if (xshm.pixmap) { gdk_drawable_get_size(xshm.pixmap, &i, &j); fprintf(stderr, "\tpixmap: %d x %d\n",i, j); } else fprintf(stderr, "\tpixmap: none\n"); fprintf(stderr, "\tarea: %d x %d\n", (int)xshm.hadj->page_size, (int)xshm.vadj->page_size); #endif } static void realize_event(GtkWidget *w, gpointer data) { xshm_map_size(xshm.mode, xshm.map_width, xshm.map_height); } GtkWidget * xshm_map_init(enum xshm_map_mode mode, int width, int height) { #ifdef DEBUG time_stamp(stderr); fprintf(stderr, "Map: Init (%s, %d, %d)\n", mode == XSHM_MAP_PIXMAP ? "Pixmap" : mode == XSHM_MAP_PIXBUF ? "Pixbuf" : mode == XSHM_MAP_IMAGE ? "Image" : "None", width, height); #endif if (!xshm.area) { /* TODO: Tweak scrollbar placement to be more like scrolled_window * and remove them when not necessary */ xshm.vbox = gtk_vbox_new(FALSE, 0); xshm.hbox = nh_gtk_new_and_pack(gtk_hbox_new(FALSE, 0), xshm.vbox, "", TRUE, TRUE, 0); xshm.area = nh_gtk_new_and_pack(gtk_drawing_area_new(), xshm.hbox, "", TRUE, TRUE, 0); /* FIXME: step_adjustment is 1. It should probably be one square */ xshm.hadj = GTK_ADJUSTMENT( gtk_adjustment_new(0.0, 0.0, 0.0, 1.0, 0.0, 0.0)); gtk_signal_connect(GTK_OBJECT(xshm.hadj), "value_changed", (GtkSignalFunc) scroll_event, 0); xshm.hscrollbar = gtk_hscrollbar_new(xshm.hadj); (void) nh_gtk_new_and_pack(xshm.hscrollbar, xshm.vbox, "", FALSE, FALSE, 0); xshm.vadj = GTK_ADJUSTMENT( gtk_adjustment_new(0.0, 0.0, 0.0, 1.0, 0.0, 0.0)); gtk_signal_connect(GTK_OBJECT(xshm.vadj), "value_changed", (GtkSignalFunc) scroll_event, 0); xshm.vscrollbar = gtk_vscrollbar_new(xshm.vadj); (void) nh_gtk_new_and_pack(xshm.vscrollbar, xshm.hbox, "", FALSE, FALSE, 0); #if GTK_CHECK_VERSION(1,3,2) gtk_widget_set_double_buffered(xshm.area, FALSE); #endif gtk_widget_set_events(xshm.area, GDK_EXPOSURE_MASK | GDK_BUTTON_PRESS_MASK); gtk_signal_connect(GTK_OBJECT(xshm.area), "configure_event", GTK_SIGNAL_FUNC(configure_event), 0); gtk_signal_connect(GTK_OBJECT(xshm.area), "expose_event", GTK_SIGNAL_FUNC(expose_event), 0); gtk_signal_connect(GTK_OBJECT(xshm.area), "realize", GTK_SIGNAL_FUNC(realize_event), 0); gtk_widget_ref(xshm.area); } if (mode != XSHM_MAP_NONE) { if (GTK_WIDGET_REALIZED(xshm.area)) xshm_map_size(mode, width, height); else { xshm.mode = mode; xshm.map_width = width; xshm.map_height = height; } } return xshm.vbox; } static void button_press_event(GtkWidget *widget, GdkEventButton *event, gpointer data) { GdkEventButton map_event = *event; map_event.x += xshm.areax; map_event.y += xshm.areay; xshm.button_press(xshm_map_pixmap ? (void *)xshm_map_pixmap : xshm_map_pixbuf ? (void *)xshm_map_pixbuf : (void *)xshm_map_image, &map_event, data); } void xshm_map_button_handler(GtkSignalFunc func, gpointer data) { if (xshm.button_press) gtk_signal_disconnect_by_func(GTK_OBJECT(xshm.area), GTK_SIGNAL_FUNC(button_press_event), xshm.button_press_data); xshm.button_press = (void (*)())func; xshm.button_press_data = data; if (func) gtk_signal_connect(GTK_OBJECT(xshm.area), "button_press_event", GTK_SIGNAL_FUNC(button_press_event), data); } void xshm_map_destroy() { #ifdef DEBUG fprintf(stderr, "Map: Destroy\n"); #endif if (xshm.pixmap) { gdk_pixmap_unref(xshm.pixmap); xshm.pixmap = NULL; } if (xshm.dirty) { gdk_region_destroy(xshm.dirty); xshm.dirty = NULL; } if (xshm_map_image) { gdk_image_destroy(xshm_map_image); xshm_map_image = NULL; } if (xshm_map_pixbuf) { gdk_pixbuf_unref(xshm_map_pixbuf); xshm_map_pixmap = NULL; } if (xshm.gc) { gdk_gc_unref(xshm.gc); xshm.gc = NULL; } xshm_map_pixmap = NULL; xshm_map_button_handler(NULL, 0); if (xshm.area) { gtk_widget_unref(xshm.area); xshm.area = NULL; } xshm.map_width = xshm.map_height = 0; } void xshm_map_clear() { GdkRectangle area; g_return_if_fail(xshm_map_image != NULL || xshm_map_pixmap != NULL); if (xshm_map_image) memset(xshm_map_image->mem, 0, xshm_map_image->height * xshm_map_image->bpl); else if (xshm_map_pixmap) gdk_draw_rectangle(xshm_map_pixmap, xshm.gc, TRUE, 0, 0, xshm.map_width, xshm.map_height); else if (xshm_map_pixbuf) memset(gdk_pixbuf_get_pixels(xshm_map_pixbuf), 0, gdk_pixbuf_get_height(xshm_map_pixbuf) * gdk_pixbuf_get_rowstride(xshm_map_pixbuf)); area.x = area.y = 0; area.width = xshm.map_width; area.height = xshm.map_height; xshm_map_draw(&area); } void xshm_map_draw_image(GdkImage *src, int src_x, int src_y, int dest_x, int dest_y, int width, int height) { int i, j; unsigned char *dp, *sp; GdkRectangle area; g_return_if_fail(xshm_map_image != NULL || xshm_map_pixmap != NULL); g_return_if_fail(src != NULL); /* Clip to src */ if (src_x < 0) { width += src_x; dest_x -= src_x; src_x = 0; } if (src_y < 0) { height += src_y; dest_y -= src_y; src_y = 0; } if (src_x + width > src->width) width = src->width - src_x; if (src_y + height > src->height) height = src->height - src_y; /* Clip to dest */ if (dest_x < 0) { width += dest_x; src_x -= dest_x; dest_x = 0; } if (dest_y < 0) { height += dest_y; src_y -= dest_y; dest_y = 0; } if (dest_x + width > xshm.map_width) width = xshm.map_width - dest_x; if (dest_y + height > xshm.map_height) height = xshm.map_height - dest_y; /* Copy */ if (xshm_map_image) { if (xshm_map_image->bpp != src->bpp || xshm_map_image->byte_order != src->byte_order) for(j = 0; j < height; j++) for(i = 0; i < width; i++) gdk_image_put_pixel(xshm_map_image, dest_x + i, dest_y + j, gdk_image_get_pixel(src, src_x + i, src_y + j)); else { dp = xshm_map_image->mem + dest_y * xshm_map_image->bpl + dest_x * xshm_map_image->bpp; sp = src->mem + src_y * src->bpl + src_x * src->bpp; i = width * xshm_map_image->bpp; for(j = 0; j < height; j++) { memcpy(dp, sp, i); dp += xshm_map_image->bpl; sp += src->bpl; } } } else gdk_draw_image(xshm_map_pixmap, xshm.gc, src, src_x, src_y, dest_x, dest_y, width, height); /* Propagate changes */ area.x = dest_x; area.y = dest_y; area.width = width; area.height = height; xshm_map_draw(&area); } void xshm_map_draw_drawable(GdkDrawable *src, int src_x, int src_y, int dest_x, int dest_y, int width, int height) { int i, j; gint src_width, src_height; GdkRectangle area; g_return_if_fail(xshm_map_image != NULL || xshm_map_pixmap != NULL); g_return_if_fail(src != NULL); /* Clip to src */ if (src_x < 0) { width += src_x; dest_x -= src_x; src_x = 0; } if (src_y < 0) { height += src_y; dest_y -= src_y; src_y = 0; } gdk_drawable_get_size(src, &src_width, &src_height); if (src_x + width > src_width) width = src_width - src_x; if (src_y + height > src_height) height = src_height - src_y; /* Clip to dest */ if (dest_x < 0) { width += dest_x; src_x -= dest_x; dest_x = 0; } if (dest_y < 0) { height += dest_y; src_y -= dest_y; dest_y = 0; } if (dest_x + width > xshm.map_width) width = xshm.map_width - dest_x; if (dest_y + height > xshm.map_height) height = xshm.map_height - dest_y; /* Copy */ if (xshm_map_image) { /* [ALI] You shouldn't be doing this. If you need to draw drawables * then xshm_map should be in pixmap mode. */ GdkImage *tmp_img; g_warning("xshm_map_draw_drawable: Not in pixmap mode"); tmp_img = gdk_drawable_get_image(src, src_x, src_y, width, height); for(j = 0; j < height; j++) for(i = 0; i < width; i++) gdk_image_put_pixel(xshm_map_image, dest_x + i, dest_y + j, gdk_image_get_pixel(tmp_img, i, j)); gdk_image_destroy(tmp_img); } else gdk_draw_drawable(xshm_map_pixmap, xshm.gc, src, src_x, src_y, dest_x, dest_y, width, height); /* Propagate changes */ area.x = dest_x; area.y = dest_y; area.width = width; area.height = height; xshm_map_draw(&area); } void xshm_map_draw_pixbuf(GdkPixbuf *src, int src_x, int src_y, int dest_x, int dest_y, int width, int height) { GdkRectangle area; int src_width; int src_height; g_return_if_fail(xshm_map_pixbuf != NULL); g_return_if_fail(src != NULL); src_width = gdk_pixbuf_get_width(src); src_height = gdk_pixbuf_get_height(src); /* Clip to src */ if (src_x < 0) { width += src_x; dest_x -= src_x; src_x = 0; } if (src_y < 0) { height += src_y; dest_y -= src_y; src_y = 0; } if (src_x + width > src_width) width = src_width - src_x; if (src_y + height > src_height) height = src_height - src_y; /* Clip to dest */ if (dest_x < 0) { width += dest_x; src_x -= dest_x; dest_x = 0; } if (dest_y < 0) { height += dest_y; src_y -= dest_y; dest_y = 0; } if (dest_x + width > xshm.map_width) width = xshm.map_width - dest_x; if (dest_y + height > xshm.map_height) height = xshm.map_height - dest_y; /* Copy */ gdk_pixbuf_copy_area(src, src_x, src_y, width, height, xshm_map_pixbuf, dest_x, dest_y); /* Propagate changes */ area.x = dest_x; area.y = dest_y; area.width = width; area.height = height; xshm_map_draw(&area); }