/* -*-c-*- */ /* Copyright (C) 2001 Olivier Chapuis */ /* This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include "config.h" #include #include "libs/fvwmlib.h" #include "libs/FShape.h" #include "libs/PictureBase.h" #include "libs/Picture.h" #include "libs/PictureUtils.h" #include "libs/PictureImageLoader.h" #include "libs/FRenderInit.h" #include "libs/Graphics.h" #include "libs/Strings.h" #include "fvwm.h" #include "externs.h" #include "window_flags.h" #include "cursor.h" #include "functions.h" #include "misc.h" #include "screen.h" #include "module_interface.h" #include "borders.h" #include "icons.h" #include "ewmh.h" #include "ewmh_intern.h" /* * net icon handler */ int ewmh_WMIcon(EWMH_CMD_ARGS) { CARD32 *list = NULL; CARD32 *new_list = NULL; CARD32 *dummy = NULL; int size = 0; if (ev != NULL && HAS_EWMH_WM_ICON_HINT(fw) == EWMH_FVWM_ICON) { /* this event has been produced by fvwm itself */ return 0; } list = ewmh_AtomGetByName(FW_W(fw),"_NET_WM_ICON", EWMH_ATOM_LIST_PROPERTY_NOTIFY, &size); if (list != NULL && HAS_EWMH_WM_ICON_HINT(fw) == EWMH_NO_ICON) { /* the application have a true _NET_WM_ICON */ SET_HAS_EWMH_WM_ICON_HINT(fw, EWMH_TRUE_ICON); } if (list == NULL || HAS_EWMH_WM_ICON_HINT(fw) != EWMH_TRUE_ICON) { /* No net icon or we have set the net icon */ if (DO_EWMH_DONATE_ICON(fw) && (new_list = ewmh_SetWmIconFromPixmap( fw, list, &size, False)) != NULL) { SET_HAS_EWMH_WM_ICON_HINT(fw, EWMH_FVWM_ICON); } } else if (ev != NULL && USE_EWMH_ICON(fw)) { /* client message. the application change its net icon */ ChangeIconPixmap(fw); } if (FMiniIconsSupported) { if (list == NULL || HAS_EWMH_WM_ICON_HINT(fw) != EWMH_TRUE_ICON) { /* No net icon or we have set the net icon */ if (DO_EWMH_DONATE_MINI_ICON(fw) && (dummy = ewmh_SetWmIconFromPixmap( fw, (new_list != NULL)? new_list : list, &size, True)) != NULL) { SET_HAS_EWMH_WM_ICON_HINT( fw, EWMH_FVWM_ICON); free(dummy); } } else { /* the application has a true ewmh icon */ if (EWMH_SetIconFromWMIcon(fw, list, size, True)) { SET_HAS_EWMH_MINI_ICON(fw, True); } } } if (list != NULL) { free(list); } if (new_list != NULL) { free(new_list); } return 0; } /* * update */ void EWMH_DoUpdateWmIcon(FvwmWindow *fw, Bool mini_icon, Bool icon) { CARD32 *list = NULL; CARD32 *new_list = NULL; CARD32 *dummy = NULL; int size = 0; Bool icon_too = False; if (HAS_EWMH_WM_ICON_HINT(fw) == EWMH_TRUE_ICON) { return; } /* first see if we have to delete */ if (FMiniIconsSupported && mini_icon && !DO_EWMH_DONATE_MINI_ICON(fw)) { if (icon && !DO_EWMH_DONATE_ICON(fw)) { icon_too = True; } EWMH_DeleteWmIcon(fw, True, icon_too); } if (!icon_too && icon && !DO_EWMH_DONATE_ICON(fw)) { EWMH_DeleteWmIcon(fw, False, True); } /* now set if needed */ if ((mini_icon && DO_EWMH_DONATE_MINI_ICON(fw)) || (icon && DO_EWMH_DONATE_ICON(fw))) { list = ewmh_AtomGetByName( FW_W(fw),"_NET_WM_ICON", EWMH_ATOM_LIST_PROPERTY_NOTIFY, &size); } else { return; } /* we have to reset */ if (icon && DO_EWMH_DONATE_ICON(fw)) { if ((new_list = ewmh_SetWmIconFromPixmap( fw, list, &size, False)) != NULL) { SET_HAS_EWMH_WM_ICON_HINT(fw, EWMH_FVWM_ICON); } } if (FMiniIconsSupported && mini_icon && DO_EWMH_DONATE_MINI_ICON(fw)) { if ((dummy = ewmh_SetWmIconFromPixmap( fw, (new_list != NULL)? new_list : list, &size, True)) != NULL) { SET_HAS_EWMH_WM_ICON_HINT(fw, EWMH_FVWM_ICON); free(dummy); } } if (list != NULL) { free(list); } if (new_list != NULL) { free(new_list); } } /* * build and set a net icon from a pixmap */ CARD32 *ewmh_SetWmIconFromPixmap( FvwmWindow *fw, CARD32 *orig_icon, int *orig_size, Bool is_mini_icon) { CARD32 *new_icon = NULL; int keep_start = 0, keep_length = 0; int width = 0, height = 0; int i,j,k,l,m; int s; Pixmap pixmap = None; Pixmap mask = None; Pixmap alpha = None; XImage *image; XImage *m_image = NULL; XImage *a_image = NULL; int save_picture_w_g_width = 0; int save_picture_w_g_height = 0; int save_icon_depth = 0; Pixmap save_icon_pixmap = None; Pixmap save_icon_mask = None; Pixmap save_icon_alpha = None; int save_icon_nalloc_pixels = 0; Pixel *save_icon_alloc_pixels = NULL; int save_icon_no_limit = 0; Window save_icon_pixmap_w = None; Bool is_pixmap_ours = False; Bool is_icon_ours = False; Bool is_icon_shaped = False; Bool destroy_icon_pix = False; s = *orig_size / sizeof(CARD32); *orig_size = 0; if (is_mini_icon) { if (FMiniIconsSupported && fw->mini_icon != NULL) { pixmap = fw->mini_icon->picture; mask = fw->mini_icon->mask; alpha = fw->mini_icon->alpha; width = fw->mini_icon->width; height = fw->mini_icon->height; } } else { /* should save and restore any iformation modified by * a call to GetIconPicture */ save_picture_w_g_width = fw->icon_g.picture_w_g.width; save_picture_w_g_height = fw->icon_g.picture_w_g.height; save_icon_depth = fw->iconDepth; save_icon_pixmap = fw->iconPixmap; save_icon_mask = fw->icon_maskPixmap; save_icon_alpha = fw->icon_alphaPixmap; save_icon_nalloc_pixels = fw->icon_nalloc_pixels; save_icon_alloc_pixels = fw->icon_alloc_pixels; save_icon_no_limit = fw->icon_no_limit; save_icon_pixmap_w = FW_W_ICON_PIXMAP(fw); is_pixmap_ours = IS_PIXMAP_OURS(fw); is_icon_ours = IS_ICON_OURS(fw); is_icon_shaped = IS_ICON_SHAPED(fw); GetIconPicture(fw, True); if (IS_PIXMAP_OURS(fw)) { destroy_icon_pix = True; } pixmap = fw->iconPixmap; mask = fw->icon_maskPixmap; alpha = fw->icon_alphaPixmap; width = fw->icon_g.picture_w_g.width; height = fw->icon_g.picture_w_g.height; if (fw->icon_alloc_pixels != NULL) { if (fw->icon_nalloc_pixels != 0) { PictureFreeColors( dpy, Pcmap, fw->icon_alloc_pixels, fw->icon_nalloc_pixels, 0, fw->icon_no_limit); } free(fw->icon_alloc_pixels); } fw->icon_g.picture_w_g.width = save_picture_w_g_width; fw->icon_g.picture_w_g.height = save_picture_w_g_height; fw->iconDepth = save_icon_depth; fw->iconPixmap = save_icon_pixmap; fw->icon_maskPixmap = save_icon_mask; fw->icon_alphaPixmap = save_icon_alpha; fw->icon_nalloc_pixels = save_icon_nalloc_pixels; fw->icon_alloc_pixels = save_icon_alloc_pixels; fw->icon_no_limit = save_icon_no_limit; FW_W_ICON_PIXMAP(fw) = save_icon_pixmap_w; SET_ICON_OURS(fw, is_icon_ours); SET_PIXMAP_OURS(fw, is_pixmap_ours); SET_ICON_SHAPED(fw, is_icon_shaped); } if (pixmap == None) { return NULL; } if (FMiniIconsSupported && orig_icon != NULL) { int k_width = (is_mini_icon)? fw->ewmh_icon_width : fw->ewmh_mini_icon_width; int k_height = (is_mini_icon)? fw->ewmh_icon_height : fw->ewmh_mini_icon_height; for (i = 0; i < s - 1; ) { if (i + 1 + orig_icon[i]*orig_icon[i+1] < s) { if (orig_icon[i] == k_width && orig_icon[i+1] == k_height) { keep_start = i; keep_length = 2 + orig_icon[i] * orig_icon[i+1]; i = s; } } if (i != s && orig_icon[i]*orig_icon[i+1] > 0) { i = i + 2 + orig_icon[i]*orig_icon[i+1]; } else { i = s; } } } image = XGetImage( dpy, pixmap, 0, 0, width, height, AllPlanes, ZPixmap); if (image == NULL) { fvwm_msg( ERR, "EWMH_SetWmIconFromPixmap", "cannot create XImage\n"); if (destroy_icon_pix) { XFreePixmap(dpy, pixmap); if (mask != None) { XFreePixmap(dpy, mask); } if (alpha != None) { XFreePixmap(dpy, alpha); } } return NULL; } if (mask != None) { m_image = XGetImage(dpy, mask, 0, 0, width, height, AllPlanes, ZPixmap); } if (alpha != None) { a_image = XGetImage(dpy, alpha, 0, 0, width, height, AllPlanes, ZPixmap); } *orig_size = (height*width + 2 + keep_length) * sizeof(CARD32); new_icon = (CARD32 *)safemalloc(*orig_size); if (keep_length > 0) { memcpy(new_icon, &orig_icon[keep_start], keep_length * sizeof(CARD32)); } new_icon[keep_length] = width; new_icon[1+keep_length] = height; k = 0; l = (2 + keep_length); m = 0; switch(image->depth) { case 1: { XColor colors[2]; CARD32 fg, bg; colors[0].pixel = fw->colors.fore; colors[1].pixel = fw->colors.back; XQueryColors(dpy, Pcmap, colors, 2); fg = 0xff000000 + (((colors[0].red >> 8) & 0xff) << 16) + (((colors[0].green >> 8) & 0xff) << 8) + ((colors[0].blue >> 8) & 0xff); bg = 0xff000000 + (((colors[1].red >> 8) & 0xff) << 16) + (((colors[1].green >> 8) & 0xff) << 8) + ((colors[1].blue >> 8) & 0xff); for (j = 0; j < height; j++) { for (i = 0; i < width; i++) { if (m_image != NULL && (XGetPixel(m_image, i, j) == 0)) { new_icon[l++] = 0; } else if (XGetPixel(image, i, j) == 0) { new_icon[l++] = bg; } else { new_icon[l++] = fg; } } } break; } default: /* depth = Pdepth */ { unsigned char *cm; XColor *colors; colors = (XColor *)safemalloc(width * height * sizeof(XColor)); cm = (unsigned char *)safemalloc( width * height * sizeof(char)); for (j = 0; j < height; j++) { for (i = 0; i < width; i++) { if (m_image != NULL && (XGetPixel(m_image, i, j) == 0)) { cm[m++] = 0; } else if (a_image != NULL) { cm[m++] = (unsigned char)XGetPixel( a_image, i, j); colors[k++].pixel = XGetPixel( image, i, j); } else { cm[m++] = 255; colors[k++].pixel = XGetPixel( image, i, j); } } } for (i = 0; i < k; i += 256) XQueryColors(dpy, Pcmap, &colors[i], min(k - i, 256)); k = 0;m = 0; for (j = 0; j < height; j++) { for (i = 0; i < width; i++) { if (cm[m] > 0) { new_icon[l++] = ((cm[m] & 0xff) << 24) + (((colors[k].red >> 8) & 0xff) << 16) + (((colors[k].green >> 8) & 0xff) << 8) + ((colors[k].blue >> 8) & 0xff); k++; } else { new_icon[l++] = 0; } m++; } } free(colors); free(cm); break; } } /* switch */ if (is_mini_icon) { fw->ewmh_mini_icon_width = width; fw->ewmh_mini_icon_height = height; } else { fw->ewmh_icon_width = width; fw->ewmh_icon_height = height; } ewmh_ChangeProperty( FW_W(fw), "_NET_WM_ICON", EWMH_ATOM_LIST_PROPERTY_NOTIFY, (unsigned char *)new_icon, height*width + 2 + keep_length); if (destroy_icon_pix) { XFreePixmap(dpy, pixmap); if (mask != None) { XFreePixmap(dpy, mask); } if (alpha != None) { XFreePixmap(dpy, alpha); } } XDestroyImage(image); if (m_image != None) { XDestroyImage(m_image); } if (a_image != None) { XDestroyImage(a_image); } return new_icon; } /* * delete the mini icon and/or the icon from a ewmh icon */ void EWMH_DeleteWmIcon(FvwmWindow *fw, Bool mini_icon, Bool icon) { CARD32 *list; CARD32 *new_list = NULL; int keep_start = 0, keep_length = 0; int s; int i; if (mini_icon && icon) { ewmh_DeleteProperty( FW_W(fw), "_NET_WM_ICON", EWMH_ATOM_LIST_PROPERTY_NOTIFY); fw->ewmh_mini_icon_width = 0; fw->ewmh_mini_icon_height = 0; fw->ewmh_icon_width = 0; fw->ewmh_icon_height = 0; /*SET_HAS_EWMH_WM_ICON_HINT(fw, EWMH_NO_ICON);*/ return; } list = ewmh_AtomGetByName( FW_W(fw),"_NET_WM_ICON", EWMH_ATOM_LIST_PROPERTY_NOTIFY, &s); if (list == NULL) { return; } s = s / sizeof(CARD32); if (FMiniIconsSupported && list != NULL) { int k_width = (mini_icon) ? fw->ewmh_icon_width : fw->ewmh_mini_icon_width; int k_height = (mini_icon) ? fw->ewmh_icon_height : fw->ewmh_mini_icon_height; for (i = 0; i < s - 1; ) { if (i + 1 + list[i]*list[i+1] < s) { if (list[i] == k_width && list[i+1] == k_height) { keep_start = i; keep_length = 2 + list[i]*list[i+1]; i = s; } } if (i != s && list[i]*list[i+1] > 0) { i = i + 2 + list[i]*list[i+1]; } else { i = s; } } } if (keep_length > 0) { new_list = (CARD32 *)safemalloc(keep_length * sizeof(CARD32)); memcpy( new_list, &list[keep_start], keep_length * sizeof(CARD32)); } if (new_list != NULL) { ewmh_ChangeProperty( FW_W(fw),"_NET_WM_ICON", EWMH_ATOM_LIST_PROPERTY_NOTIFY, (unsigned char *)new_list, keep_length); } else { /*SET_HAS_EWMH_WM_ICON_HINT(fw, EWMH_NO_ICON);*/ ewmh_DeleteProperty( FW_W(fw), "_NET_WM_ICON", EWMH_ATOM_LIST_PROPERTY_NOTIFY); } if (mini_icon) { fw->ewmh_mini_icon_width = 0; fw->ewmh_mini_icon_height = 0; } if (icon) { fw->ewmh_icon_width = 0; fw->ewmh_icon_height = 0; } if (new_list != NULL) { free(new_list); } free(list); } /* * Create an x image from a NET icon */ #define SQUARE(X) ((X)*(X)) static void extract_wm_icon( CARD32 *list, int size, int wanted_w, int wanted_h, int *start_best, int *best_w, int *best_h) { int i; int dist = 0; *start_best = 0; *best_w = 0; *best_h = 0; size = size / (sizeof(CARD32)); for (i = 0; i < size - 1; ) { if (i + 1 + list[i]*list[i+1] < size) { if (*best_w == 0 && *best_h == 0) { *start_best = i+2; *best_w = list[i]; *best_h = list[i+1]; dist = SQUARE( list[i]-wanted_w) + SQUARE(list[i+1]-wanted_h); } else if (SQUARE(list[i]-wanted_w) + SQUARE(list[i+1]-wanted_h) < dist) { *start_best = i+2; *best_w = list[i]; *best_h = list[i+1]; dist = SQUARE( list[i]-wanted_w) + SQUARE(list[i+1]-wanted_h); } } if (list[i]*list[i+1] > 0) { i = i + 2 + list[i]*list[i+1]; } else { i = size; } } return; } #define MINI_ICON_WANTED_WIDTH 16 #define MINI_ICON_WANTED_HEIGHT 16 #define MINI_ICON_MAX_WIDTH 22 #define MINI_ICON_MAX_HEIGHT 22 #define ICON_WANTED_WIDTH 56 #define ICON_WANTED_HEIGHT 56 #define ICON_MAX_WIDTH 100 #define ICON_MAX_HEIGHT 100 int EWMH_SetIconFromWMIcon( FvwmWindow *fw, CARD32 *list, int size, Bool is_mini_icon) { int start, width, height; int wanted_w, wanted_h; int max_w, max_h; Pixmap pixmap = None; Pixmap mask = None; Pixmap alpha = None; Bool free_list = False; int nalloc_pixels; Pixel *alloc_pixels; int no_limit; FvwmPictureAttributes fpa; if (list == NULL) { /* we are called from icons.c or update.c */ list = ewmh_AtomGetByName( FW_W(fw),"_NET_WM_ICON", EWMH_ATOM_LIST_PROPERTY_NOTIFY, &size); free_list = True; if (list == NULL) { return 0; } } if (is_mini_icon) { wanted_w = MINI_ICON_WANTED_WIDTH; wanted_h = MINI_ICON_WANTED_HEIGHT; max_w = MINI_ICON_MAX_WIDTH; max_h = ICON_MAX_HEIGHT; fpa.mask = 0; } else { wanted_w = ICON_WANTED_WIDTH; wanted_h = ICON_WANTED_HEIGHT; max_w = ICON_MAX_WIDTH; max_h = ICON_MAX_HEIGHT; if (fw->cs >= 0 && Colorset[fw->cs].do_dither_icon) { fpa.mask = FPAM_DITHER; } else { fpa.mask = 0; } } extract_wm_icon( list, size, wanted_w, wanted_h, &start, &width, &height); if (width == 0 || height == 0) { if (free_list) { free(list); } return 0; } if (!PImageCreatePixmapFromArgbData( dpy, Scr.NoFocusWin, list, start, width, height, &pixmap, &mask, &alpha, &nalloc_pixels, &alloc_pixels, &no_limit, fpa)) { fvwm_msg(ERR, "EWMH_SetIconFromWMIcon", "fail to create a pixmap\n"); if (free_list) { free(list); } return 0; } if (width > max_w || height > max_h) { Pixmap np = None,nm =None, na = None; if (pixmap) { np = CreateStretchPixmap( dpy, pixmap, width, height, Pdepth, wanted_w, wanted_h, Scr.TitleGC); XFreePixmap(dpy, pixmap); pixmap = np; } if (mask) { nm = CreateStretchPixmap( dpy, mask, width, height, 1, wanted_w, wanted_h, Scr.MonoGC); XFreePixmap(dpy, mask); mask = nm; } if (alpha) { na = CreateStretchPixmap( dpy, alpha, width, height, FRenderGetAlphaDepth(), wanted_w, wanted_h, Scr.AlphaGC); XFreePixmap(dpy, alpha); alpha = na; } width = wanted_w; height = wanted_h; } if (FMiniIconsSupported && is_mini_icon && !DO_EWMH_MINI_ICON_OVERRIDE(fw)) { char *name = NULL; CopyString(&name,"ewmh_mini_icon"); if (fw->mini_icon) { PDestroyFvwmPicture(dpy,fw->mini_icon); fw->mini_icon = 0; } fw->mini_icon = PCacheFvwmPictureFromPixmap( dpy, Scr.NoFocusWin, name, pixmap,mask,alpha, width, height, nalloc_pixels, alloc_pixels, no_limit); if (fw->mini_icon != NULL) { fw->mini_pixmap_file = name; BroadcastFvwmPicture( M_MINI_ICON, FW_W(fw), FW_W_FRAME(fw), (unsigned long)fw, fw->mini_icon, fw->mini_pixmap_file); border_redraw_decorations(fw); } } if (!is_mini_icon) { fw->iconPixmap = pixmap; fw->icon_maskPixmap = mask; fw->icon_alphaPixmap = alpha; fw->icon_nalloc_pixels = nalloc_pixels; fw->icon_alloc_pixels = alloc_pixels; fw->icon_no_limit = no_limit; fw->icon_g.picture_w_g.width = width; fw->icon_g.picture_w_g.height = height; fw->iconDepth = Pdepth; SET_PIXMAP_OURS(fw, 1); if (FShapesSupported && mask) { SET_ICON_SHAPED(fw, 1); } } if (free_list) { free(list); } return 1; }