/* * ColorPanel for WINGs * * by ]d : Original idea and basic initial code * Pascal Hofstee : Code for wheeldrawing and calculating * colors from it. * Primary coder of this Color Panel. * Alban Hertroys : Optimizations for algorithms for color- * wheel. Also custom ColorPalettes and * magnifying glass. Secondary coder ;) * Alfredo K. Kojima : For pointing out memory-allocation * problems and similair code-issues * Marco van Hylckama-Vlieg : For once again doing the artwork ;-) * */ /* TODO: * - Look at further optimization of colorWheel matrix calculation. * It appears to be rather symmetric in angles of 60 degrees, * while it is optimized in angles of 90 degrees. * - Custom color-lists and custom colors in custom color-lists. * - Stored colors */ #include "wconfig.h" #include "WINGsP.h" #include "wcolorpanelP.h" #include "WINGs/WINGsX11.h" #include #include #ifdef USE_XSHAPE #include #endif /* Forward declaration from wcolorpanel.c */ static unsigned char getShift(unsigned char value); static unsigned char Cursor_bits[] = { 0x00, 0x7e, 0x00, 0x00, 0xc0, 0x81, 0x03, 0x00, 0x20, 0x00, 0x04, 0x00, 0x10, 0x00, 0x08, 0x00, 0x08, 0x00, 0x10, 0x00, 0x04, 0x00, 0x20, 0x00, 0x02, 0x00, 0x40, 0x00, 0x02, 0x00, 0x40, 0x00, 0x02, 0x00, 0x40, 0x00, 0x01, 0x42, 0x80, 0x00, 0x01, 0x24, 0x80, 0x00, 0x01, 0x00, 0x80, 0x00, 0x01, 0x00, 0x80, 0x00, 0x01, 0x24, 0x80, 0x00, 0x01, 0x42, 0x80, 0x00, 0x02, 0x00, 0x40, 0x00, 0x02, 0x00, 0x40, 0x00, 0x02, 0x00, 0x40, 0x00, 0x04, 0x00, 0x20, 0x00, 0x08, 0x00, 0x50, 0x00, 0x10, 0x00, 0x88, 0x00, 0x20, 0x00, 0x5c, 0x01, 0xc0, 0x81, 0x3b, 0x02, 0x00, 0x7e, 0x70, 0x05, 0x00, 0x00, 0xe0, 0x08, 0x00, 0x00, 0xc0, 0x15, 0x00, 0x00, 0x80, 0x23, 0x00, 0x00, 0x00, 0x57, 0x00, 0x00, 0x00, 0x8e, 0x00, 0x00, 0x00, 0x5c, 0x00, 0x00, 0x00, 0xb8, 0x00, 0x00, 0x00, 0x70 }; static unsigned char Cursor_shape_bits[] = { 0x00, 0x7e, 0x00, 0x00, 0xc0, 0x81, 0x03, 0x00, 0x20, 0x00, 0x04, 0x00, 0x10, 0x00, 0x08, 0x00, 0x08, 0x00, 0x10, 0x00, 0x04, 0x00, 0x20, 0x00, 0x02, 0x00, 0x40, 0x00, 0x02, 0x00, 0x40, 0x00, 0x02, 0x00, 0x40, 0x00, 0x01, 0x42, 0x80, 0x00, 0x01, 0x24, 0x80, 0x00, 0x01, 0x00, 0x80, 0x00, 0x01, 0x00, 0x80, 0x00, 0x01, 0x24, 0x80, 0x00, 0x01, 0x42, 0x80, 0x00, 0x02, 0x00, 0x40, 0x00, 0x02, 0x00, 0x40, 0x00, 0x02, 0x00, 0x40, 0x00, 0x04, 0x00, 0x20, 0x00, 0x08, 0x00, 0x70, 0x00, 0x10, 0x00, 0xf8, 0x00, 0x20, 0x00, 0xfc, 0x01, 0xc0, 0x81, 0xfb, 0x03, 0x00, 0x7e, 0xf0, 0x07, 0x00, 0x00, 0xe0, 0x0f, 0x00, 0x00, 0xc0, 0x1f, 0x00, 0x00, 0x80, 0x3f, 0x00, 0x00, 0x00, 0x7f, 0x00, 0x00, 0x00, 0xfe, 0x00, 0x00, 0x00, 0xfc, 0x00, 0x00, 0x00, 0xf8, 0x00, 0x00, 0x00, 0x70 }; /* Clip-mask for magnified pixels */ #define Cursor_mask_width 24 #define Cursor_mask_height 24 static unsigned char Cursor_mask_bits[] = { 0x00, 0x00, 0x00, 0x00, 0x7e, 0x00, 0xc0, 0xff, 0x03, 0xe0, 0xff, 0x07, 0xf0, 0xff, 0x0f, 0xf8, 0xff, 0x1f, 0xfc, 0xff, 0x3f, 0xfc, 0xff, 0x3f, 0xfc, 0xff, 0x3f, 0xfe, 0xff, 0x7f, 0xfe, 0xff, 0x7f, 0xfe, 0xff, 0x7f, 0xfe, 0xff, 0x7f, 0xfe, 0xff, 0x7f, 0xfe, 0xff, 0x7f, 0xfc, 0xff, 0x3f, 0xfc, 0xff, 0x3f, 0xfc, 0xff, 0x3f, 0xf8, 0xff, 0x1f, 0xf0, 0xff, 0x0f, 0xe0, 0xff, 0x07, 0xc0, 0xff, 0x03, 0x00, 0x7e, 0x00, 0x00, 0x00, 0x00 }; typedef struct MovingView { WMView *view; /* The view this is all about */ XImage *image; /* What's under the view */ XImage *dirtyRect; /* Storage of overlapped image area */ WMNativePixmap magPix; /* Magnified part of pixmap */ RColor color; /* Color of a pixel in the image */ int x, y; /* Position of view */ } MovingView; /* Cursor hotspot and size constants */ #define Cursor_x_hot 11 #define Cursor_y_hot 11 #define Cursor_width 32 #define Cursor_height 32 static RColor ulongToRColor(WMScreen * scr, unsigned long value); static WMNativeCursor magnifyGrabPointer(W_ColorPanel *panel); static WMPoint magnifyInitialize(W_ColorPanel *panel); static WMNativePixmap magnifyCreatePixmap(WMColorPanel * panel); static void magnifyGetImageStored(W_ColorPanel *panel, int x1, int y1, int x2, int y2); static XImage *magnifyGetImage(WMScreen * scr, XImage * image, int x, int y, int w, int h); /****************** Magnifying Cursor Functions *******************/ static XImage *magnifyGetImage(WMScreen * scr, XImage * image, int x, int y, int w, int h) { int x0 = 0, y0 = 0, w0 = w, h0 = h; const int displayWidth = DisplayWidth(scr->display, scr->screen), displayHeight = DisplayHeight(scr->display, scr->screen); if (!(image && image->data)) { /* The image in ( (MovingView *)panel->magnifyGlass )->image does not exist yet. * Grab one from the screen (not beyond) and use it from now on. */ if (!(image = XGetImage(scr->display, scr->rootWin, x - Cursor_x_hot, y - Cursor_y_hot, w, h, AllPlanes, ZPixmap))) wwarning(_("Color Panel: X failed request")); return image; } /* Coordinate correction for back pixmap * if magnifying glass is at screen-borders */ /* Figure 1: Shifting of rectangle-to-grab at top/left screen borders * Hatched area is beyond screen border. * * |<-Cursor_x_hot->| * ________________|_____ * |/ / / / / / /| | | * | / / / / / / |(x,y) | * |/_/_/_/_/_/_/|________| * |<----x0----->|<--w0-->| * 0 */ /* Figure 2: Shifting of rectangle-to-grab at bottom/right * screen borders * Hatched area is beyond screen border * * |<-Cursor_x_hot->| * ________________|_______________ * | | | / / / / / /| * | (x,y)|/ / / / / / | * |___________________|_/_/_/_/_/_/| * |<-------w0-------->| | * |<---------------w--|----------->| * | | * x0 Displaywidth-1 */ if (x < Cursor_x_hot) { /* see fig. 1 */ x0 = Cursor_x_hot - x; w0 = w - x0; } if (displayWidth - 1 < x - Cursor_x_hot + w) { /* see fig. 2 */ w0 = (displayWidth) - (x - Cursor_x_hot); } if (y < Cursor_y_hot) { /* see fig. 1 */ y0 = Cursor_y_hot - y; h0 = h - y0; } if (displayHeight - 1 < y - Cursor_y_hot + h) { /* see fig. 2 */ h0 = (displayHeight) - (y - Cursor_y_hot); } /* end of coordinate correction */ /* Grab an image from the screen, clipped if necessary, * and put it in the existing ( (MovingView *)panel->magnifyGlass )->image * with the corresponding clipping offset. */ if (!XGetSubImage(scr->display, scr->rootWin, x - Cursor_x_hot + x0, y - Cursor_y_hot + y0, w0, h0, AllPlanes, ZPixmap, image, x0, y0)) wwarning(_("Color Panel: X failed request")); return NULL; } static void magnifyGetImageStored(WMColorPanel * panel, int x1, int y1, int x2, int y2) { /* (x1, y1) = topleft corner of existing rectangle * (x2, y2) = topleft corner of new position */ W_Screen *scr = WMWidgetScreen(panel->win); int xa = 0, ya = 0, xb = 0, yb = 0; int width, height; const int dx = abs(x2 - x1), dy = abs(y2 - y1); XImage *image; const int x_min = Cursor_x_hot, y_min = Cursor_y_hot, x_max = DisplayWidth(scr->display, scr->screen) - 1 - (Cursor_mask_width - Cursor_x_hot), y_max = DisplayHeight(scr->display, scr->screen) - 1 - (Cursor_mask_height - Cursor_y_hot); if ((dx == 0) && (dy == 0) && ( (MovingView *)panel->magnifyGlass )->image) return; /* No movement */ if (x1 < x2) xa = dx; else xb = dx; if (y1 < y2) ya = dy; else yb = dy; width = Cursor_mask_width - dx; height = Cursor_mask_height - dy; /* If the traversed distance is larger than the size of the magnifying * glass contents, there is no need to do dirty rectangles. A whole new * rectangle can be grabbed (unless that rectangle falls partially * off screen). * Destroying the image and setting it to NULL will achieve that later on. * * Of course, grabbing an XImage beyond the borders of the screen will * cause trouble, this is considdered a special case. Part of the screen * is grabbed, but there is no need for dirty rectangles. */ if ((width <= 0) || (height <= 0)) { if ((x2 >= x_min) && (y2 >= y_min) && (x2 <= x_max) && (y2 <= y_max)) { if (( (MovingView *)panel->magnifyGlass )->image) XDestroyImage(( (MovingView *)panel->magnifyGlass )->image); ( (MovingView *)panel->magnifyGlass )->image = NULL; } } else { if (( (MovingView *)panel->magnifyGlass )->image) { /* Get dirty rectangle from ( (MovingView *)panel->magnifyGlass )->image */ ( (MovingView *)panel->magnifyGlass )->dirtyRect = XSubImage(( (MovingView *)panel->magnifyGlass )->image, xa, ya, width, height); if (!( (MovingView *)panel->magnifyGlass )->dirtyRect) { wwarning(_("Color Panel: X failed request")); return; /* X returned a NULL from XSubImage */ } } } /* Get image from screen */ image = magnifyGetImage(scr, ( (MovingView *)panel->magnifyGlass )->image, x2, y2, Cursor_mask_width, Cursor_mask_height); if (image) { /* Only reassign if a *new* image was grabbed */ ( (MovingView *)panel->magnifyGlass )->image = image; return; } /* Copy previously stored rectangle on covered part of image */ if (( (MovingView *)panel->magnifyGlass )->image && ( (MovingView *)panel->magnifyGlass )->dirtyRect) { int old_height; /* "width" and "height" are used as coordinates here, * and run from [0...width-1] and [0...height-1] respectively. */ width--; height--; old_height = height; for (; width >= 0; width--) for (height = old_height; height >= 0; height--) XPutPixel(( (MovingView *)panel->magnifyGlass )->image, xb + width, yb + height, XGetPixel(( (MovingView *)panel->magnifyGlass )->dirtyRect, width, height)); XDestroyImage(( (MovingView *)panel->magnifyGlass )->dirtyRect); ( (MovingView *)panel->magnifyGlass )->dirtyRect = NULL; } return; } static WMNativePixmap magnifyCreatePixmap(WMColorPanel * panel) { W_Screen *scr = WMWidgetScreen(panel->win); int u, v; #ifndef USE_XSHAPE WMNativePixmap pixmap; #endif unsigned long color; if (!( (MovingView *)panel->magnifyGlass )->image) return WM_NATIVE_NONE; if (!( (MovingView *)panel->magnifyGlass )->magPix) return WM_NATIVE_NONE; /* * Copy an area of only 5x5 pixels from the center of the image. */ for (u = 0; u < 5; u++) { for (v = 0; v < 5; v++) { color = XGetPixel(( (MovingView *)panel->magnifyGlass )->image, u + 9, v + 9); XSetForeground(scr->display, scr->copyGC, color); if ((u == 2) && (v == 2)) /* (2,2) is center pixel (unmagn.) */ ( (MovingView *)panel->magnifyGlass )->color = ulongToRColor(scr, color); /* The center square must eventually be centered around the * hotspot. The image needs shifting to achieve this. The amount of * shifting is (Cursor_mask_width/2 - 2 * square_size) = 11-10 = 1 * _ _ _ _ _ * |_|_|_|_|_| * ^------- center of center square == Cursor_x_hot */ XFillRectangle(scr->display, ( (MovingView *)panel->magnifyGlass )->magPix, scr->copyGC, u * 5 + (u == 0 ? 0 : -1), v * 5 + (v == 0 ? 0 : -1), (u == 0 ? 4 : 5), (v == 0 ? 4 : 5)); } } #ifdef USE_XSHAPE return ( (MovingView *)panel->magnifyGlass )->magPix; #else pixmap = XCreatePixmap(scr->display, W_DRAWABLE(scr), Cursor_mask_width, Cursor_mask_height, scr->depth); if (!pixmap) return WM_NATIVE_NONE; XPutImage(scr->display, pixmap, scr->copyGC, ( (MovingView *)panel->magnifyGlass )->image, 0, 0, 0, 0, Cursor_mask_width, Cursor_mask_height); /* Copy the magnified pixmap, with the clip mask, to background pixmap */ XCopyArea(scr->display, ( (MovingView *)panel->magnifyGlass )->magPix, pixmap, scr->clipGC, 0, 0, Cursor_mask_width, Cursor_mask_height, 0, 0); /* (2,2) puts center pixel on center of glass */ return pixmap; #endif } static WMView *magnifyCreateView(W_ColorPanel *panel) { W_Screen *scr = WMWidgetScreen(panel->win); WMView *magView; magView = W_CreateTopView(scr); if (!magView) return NULL; magView->self = panel->win; magView->flags.topLevel = 1; magView->attribFlags |= CWOverrideRedirect | CWSaveUnder; magView->attribs.override_redirect = True; magView->attribs.save_under = True; W_ResizeView(magView, Cursor_mask_width, Cursor_mask_height); W_RealizeView(magView); return magView; } static WMNativeCursor magnifyGrabPointer(W_ColorPanel *panel) { W_Screen *scr = WMWidgetScreen(panel->win); WMNativePixmap magPixmap, magPixmap2; WMNativeCursor magCursor; XColor fgColor = { 0, 0, 0, 0, DoRed | DoGreen | DoBlue, 0 }; XColor bgColor = { 0, 0xbf00, 0xa000, 0x5000, DoRed | DoGreen | DoBlue, 0 }; /* WMNativeCursor creation stuff */ magPixmap = XCreatePixmapFromBitmapData(scr->display, W_DRAWABLE(scr), (char *)Cursor_bits, Cursor_width, Cursor_height, 1, 0, 1); magPixmap2 = XCreatePixmapFromBitmapData(scr->display, W_DRAWABLE(scr), (char *)Cursor_shape_bits, Cursor_width, Cursor_height, 1, 0, 1); magCursor = XCreatePixmapCursor(scr->display, magPixmap, magPixmap2, &fgColor, &bgColor, Cursor_x_hot, Cursor_y_hot); XFreePixmap(scr->display, magPixmap); XFreePixmap(scr->display, magPixmap2); XRecolorCursor(scr->display, magCursor, &fgColor, &bgColor); /* Set up Pointer */ XGrabPointer(scr->display, ( (MovingView *)panel->magnifyGlass )->view->window, True, WM_EVMASK_POINTER_MOTION | WM_EVMASK_BUTTON_PRESS, WM_GRAB_MODE_ASYNC, WM_GRAB_MODE_ASYNC, scr->rootWin, magCursor, WM_CURRENT_TIME); return magCursor; } static WMPoint magnifyInitialize(W_ColorPanel *panel) { W_Screen *scr = WMWidgetScreen(panel->win); int x, y, u, v; unsigned int mask; WMNativePixmap pixmap, clip_mask; WMPoint point; WNativeWindow root_return, child_return; clip_mask = XCreatePixmapFromBitmapData(scr->display, W_DRAWABLE(scr), (char *)Cursor_mask_bits, Cursor_mask_width, Cursor_mask_height, 1, 0, 1); ( (MovingView *)panel->magnifyGlass )->magPix = XCreatePixmap(scr->display, W_DRAWABLE(scr), 5 * 5 - 1, 5 * 5 - 1, scr->depth); XQueryPointer(scr->display, scr->rootWin, &root_return, &child_return, &x, &y, &u, &v, &mask); ( (MovingView *)panel->magnifyGlass )->image = NULL; /* Clipmask to make magnified view-contents circular */ #ifdef USE_XSHAPE XShapeCombineMask(scr->display, WMViewXID(( (MovingView *)panel->magnifyGlass )->view), ShapeBounding, 0, 0, clip_mask, ShapeSet); #else /* Clip circle in glass cursor */ XSetClipMask(scr->display, scr->clipGC, clip_mask); XSetClipOrigin(scr->display, scr->clipGC, 0, 0); #endif XFreePixmap(scr->display, clip_mask); /* Draw initial magnifying glass contents */ magnifyGetImageStored(panel, x, y, x, y); pixmap = magnifyCreatePixmap(panel); XSetWindowBackgroundPixmap(scr->display, WMViewXID(( (MovingView *)panel->magnifyGlass )->view), pixmap); XClearWindow(scr->display, WMViewXID(( (MovingView *)panel->magnifyGlass )->view)); XFlush(scr->display); #ifndef USE_XSHAPE XFreePixmap(scr->display, pixmap); #endif point.x = x; point.y = y; return point; } void W_ColorPanelMagnify(WMWidget * w, void *data) { W_ColorPanel *panel = (W_ColorPanel *) (data); W_Screen *scr = WMWidgetScreen(panel->win); WMNativeCursor magCursor; WMNativePixmap pixmap; WMEvent event; XEvent rawEvent; WMPoint initialPosition; /* Parameter not used, but tell the compiler that it is ok */ (void) w; /* Destroy wheelBackImg, so it'll update properly */ if (panel->selectionBackImg) { scr->renderBackend->pixmap_free(scr->renderBackendCtx, panel->selectionBackImg); panel->selectionBackImg = WM_NATIVE_NONE; } /* Create magnifying glass */ panel->magnifyGlass = wmalloc(sizeof(MovingView)); ( (MovingView *)panel->magnifyGlass )->view = magnifyCreateView(panel); if (!( (MovingView *)panel->magnifyGlass )->view) return; initialPosition = magnifyInitialize(panel); ( (MovingView *)panel->magnifyGlass )->x = initialPosition.x; ( (MovingView *)panel->magnifyGlass )->y = initialPosition.y; W_MoveView(( (MovingView *)panel->magnifyGlass )->view, ( (MovingView *)panel->magnifyGlass )->x - Cursor_x_hot, ( (MovingView *)panel->magnifyGlass )->y - Cursor_y_hot); W_MapView(( (MovingView *)panel->magnifyGlass )->view); magCursor = magnifyGrabPointer(panel); while (( (MovingView *)panel->magnifyGlass )->image) { WMNextEvent(scr, &event); /* Pack motion events */ while (XCheckTypedEvent(scr->display, MotionNotify, &rawEvent)) { } switch (event.type) { case WME_BUTTON_PRESS: XDestroyImage(( (MovingView *)panel->magnifyGlass )->image); ( (MovingView *)panel->magnifyGlass )->image = NULL; if (event.u.button.button == Button1) { panel->color.rgb = ( (MovingView *)panel->magnifyGlass )->color; panel->color.set = cpRGB; updateSwatch(panel, panel->color); } switch (panel->mode) { case WMWheelModeColorPanel: wheelInit(panel); wheelRender(panel); wheelPaint(panel); break; case WMGrayModeColorPanel: grayInit(panel); break; case WMRGBModeColorPanel: rgbInit(panel); break; case WMCMYKModeColorPanel: cmykInit(panel); break; case WMHSBModeColorPanel: hsbInit(panel); break; default: break; } panel->lastChanged = panel->mode; WMSetButtonSelected(panel->magnifyBtn, False); break; case WME_MOTION: while (XPending(scr->display)) { XEvent ev; XPeekEvent(scr->display, &ev); if (ev.type == MotionNotify) XNextEvent(scr->display, &rawEvent); else break; } /* Get a "dirty rectangle" */ magnifyGetImageStored(panel, ( (MovingView *)panel->magnifyGlass )->x, ( (MovingView *)panel->magnifyGlass )->y, event.u.motion.x_root, event.u.motion.y_root); /* Update coordinates */ ( (MovingView *)panel->magnifyGlass )->x = event.u.motion.x_root; ( (MovingView *)panel->magnifyGlass )->y = event.u.motion.y_root; /* Move view */ W_MoveView(( (MovingView *)panel->magnifyGlass )->view, ( (MovingView *)panel->magnifyGlass )->x - Cursor_x_hot, ( (MovingView *)panel->magnifyGlass )->y - Cursor_y_hot); /* Put new image (with magn.) in view */ pixmap = magnifyCreatePixmap(panel); if (pixmap != WM_NATIVE_NONE) { /* Change the window background */ XSetWindowBackgroundPixmap(scr->display, WMViewXID(( (MovingView *)panel->magnifyGlass )->view), pixmap); /* Force an Expose (handled by X) */ XClearWindow(scr->display, WMViewXID(( (MovingView *)panel->magnifyGlass )->view)); /* Synchronize the event queue, so the Expose is handled NOW */ XFlush(scr->display); #ifndef USE_XSHAPE XFreePixmap(scr->display, pixmap); #endif } break; /* Try XQueryPointer for this !!! It returns windows that the pointer * is over. Note: We found this solving the invisible donkey cap bug */ #if 0 /* As it is impossible to make this work in all cases, * we consider it confusing. Therefore we disabled it. */ case WME_FOCUS_OUT: /* fall through */ case WME_FOCUS_IN: /* * Color Panel window (panel->win) lost or received focus. * We need to update the pixmap in the magnifying glass. * * BUG Doesn't work with focus switches between two windows * if none of them is the color panel. */ XUngrabPointer(scr->display, WM_CURRENT_TIME); W_UnmapView(( (MovingView *)panel->magnifyGlass )->view); magnifyInitialize(panel); W_MapView(( (MovingView *)panel->magnifyGlass )->view); XGrabPointer(scr->display, ( (MovingView *)panel->magnifyGlass )->view->window, True, WM_EVMASK_POINTER_MOTION | WM_EVMASK_BUTTON_PRESS, WM_GRAB_MODE_ASYNC, WM_GRAB_MODE_ASYNC, scr->rootWin, magCursor, WM_CURRENT_TIME); break; #endif default: WMHandleEvent(&event); break; } /* of switch */ } XUngrabPointer(scr->display, WM_CURRENT_TIME); XFreeCursor(scr->display, magCursor); XFreePixmap(scr->display, ( (MovingView *)panel->magnifyGlass )->magPix); ( (MovingView *)panel->magnifyGlass )->magPix = WM_NATIVE_NONE; W_UnmapView(( (MovingView *)panel->magnifyGlass )->view); W_DestroyView(( (MovingView *)panel->magnifyGlass )->view); ( (MovingView *)panel->magnifyGlass )->view = NULL; wfree(panel->magnifyGlass); } static RColor ulongToRColor(WMScreen * scr, unsigned long value) { RColor color; XColor *xcolor = NULL; xcolor = wmalloc(sizeof(XColor)); xcolor->pixel = value; XQueryColor(scr->display, scr->rcontext->cmap, xcolor); color.red = xcolor->red >> 8; color.green = xcolor->green >> 8; color.blue = xcolor->blue >> 8; color.alpha = 0; wfree(xcolor); }