972 lines
24 KiB
C
972 lines
24 KiB
C
/* icon.c - window icon and dock and appicon parent
|
|
*
|
|
* Window Maker window manager
|
|
*
|
|
* Copyright (c) 1997-2003 Alfredo K. Kojima
|
|
*
|
|
* 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.,
|
|
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
|
*/
|
|
|
|
#include "wconfig.h"
|
|
|
|
#include <stdlib.h>
|
|
#include <stdio.h>
|
|
#include <stdint.h>
|
|
#include <string.h>
|
|
#include <unistd.h>
|
|
#include <ctype.h>
|
|
#include <wraster.h>
|
|
#include <sys/stat.h>
|
|
|
|
#include "WindowMaker.h"
|
|
#include "wcore.h"
|
|
#include "texture.h"
|
|
#include "window.h"
|
|
#include "icon.h"
|
|
#include "actions.h"
|
|
#include "dock.h"
|
|
#include "stacking.h"
|
|
#include "application.h"
|
|
#include "defaults.h"
|
|
#include "appicon.h"
|
|
#include "misc.h"
|
|
#include "startup.h"
|
|
#include "event.h"
|
|
#include "winmenu.h"
|
|
|
|
/**** Global varianebles ****/
|
|
|
|
#define MOD_MASK wPreferences.modifier_mask
|
|
#define CACHE_ICON_PATH "/" PACKAGE_TARNAME "/CachedPixmaps"
|
|
#define ICON_BORDER 3
|
|
|
|
static void miniwindowExpose(WObjDescriptor *desc, WBackendEvent *event);
|
|
static void miniwindowMouseDown(WObjDescriptor *desc, WBackendEvent *event);
|
|
static void miniwindowDblClick(WObjDescriptor *desc, WBackendEvent *event);
|
|
|
|
static WIcon *icon_create_core(WScreen *scr, int coord_x, int coord_y);
|
|
|
|
static void set_dockapp_in_icon(WIcon *icon);
|
|
static void get_rimage_icon_from_icon_win(WIcon *icon);
|
|
static void get_rimage_icon_from_user_icon(WIcon *icon);
|
|
static void get_rimage_icon_from_default_icon(WIcon *icon);
|
|
static void get_rimage_icon_from_x11(WIcon *icon);
|
|
|
|
static void icon_update_pixmap(WIcon *icon, RImage *image);
|
|
static void unset_icon_image(WIcon *icon);
|
|
|
|
/****** Notification Observers ******/
|
|
|
|
static void appearanceObserver(void *self, WMNotification *notif)
|
|
{
|
|
WIcon *icon = (WIcon *) self;
|
|
uintptr_t flags = (uintptr_t)WMGetNotificationClientData(notif);
|
|
|
|
if ((flags & WTextureSettings) || (flags & WFontSettings)) {
|
|
/* If the rimage exists, update the icon, else create it */
|
|
if (icon->file_image)
|
|
update_icon_pixmap(icon);
|
|
else
|
|
wIconPaint(icon);
|
|
}
|
|
|
|
/* so that the appicon expose handlers will paint the appicon specific
|
|
* stuff */
|
|
wm_backend->window_clear(icon->core->window);
|
|
}
|
|
|
|
static void tileObserver(void *self, WMNotification *notif)
|
|
{
|
|
WIcon *icon = (WIcon *) self;
|
|
|
|
/* Parameter not used, but tell the compiler that it is ok */
|
|
(void) notif;
|
|
|
|
update_icon_pixmap(icon);
|
|
|
|
/* For clip/drawer icons, repaint the workspace number/name text.
|
|
* update_icon_pixmap → wIconPaint renders the generic icon; the clip
|
|
* overlay (text) requires an additional clip_icon_paint call. */
|
|
WScreen *scr = icon->core->screen_ptr;
|
|
if (scr->clip_icon && scr->clip_icon->icon == icon) {
|
|
wClipIconPaint(scr->clip_icon);
|
|
}
|
|
}
|
|
|
|
/************************************/
|
|
|
|
static int getSize(WMDrawable d, unsigned int *w, unsigned int *h, unsigned int *dep)
|
|
{
|
|
WNativeWindow rjunk;
|
|
int xjunk, yjunk;
|
|
unsigned int bjunk;
|
|
|
|
return wm_backend->pixmap_get_size((unsigned long)d, w, h, dep);
|
|
}
|
|
|
|
WIcon *icon_create_for_wwindow(WWindow *wwin)
|
|
{
|
|
WScreen *scr = wwin->screen_ptr;
|
|
WIcon *icon;
|
|
|
|
icon = icon_create_core(scr, wwin->icon_x, wwin->icon_y);
|
|
|
|
icon->owner = wwin;
|
|
if (wwin->wm_hints && (wwin->wm_hints->flags & WMH_ICON_WINDOW_HINT)) {
|
|
if (wwin->client_win == wwin->main_window) {
|
|
WApplication *wapp;
|
|
/* do not let miniwindow steal app-icon's icon window */
|
|
wapp = wApplicationOf(wwin->client_win);
|
|
if (!wapp || wapp->app_icon == NULL)
|
|
icon->icon_win = wwin->wm_hints->icon_window;
|
|
} else {
|
|
icon->icon_win = wwin->wm_hints->icon_window;
|
|
}
|
|
}
|
|
#ifdef NO_MINIWINDOW_TITLES
|
|
icon->show_title = 0;
|
|
#else
|
|
icon->show_title = 1;
|
|
#endif
|
|
|
|
wIconChangeTitle(icon, wwin);
|
|
icon->tile_type = TILE_NORMAL;
|
|
|
|
set_icon_image_from_database(icon, wwin->wm_instance, wwin->wm_class, NULL);
|
|
/* Update the icon, because icon could be NULL */
|
|
wIconUpdate(icon);
|
|
|
|
WMAddNotificationObserver(appearanceObserver, icon, WNIconAppearanceSettingsChanged, icon);
|
|
WMAddNotificationObserver(tileObserver, icon, WNIconTileSettingsChanged, icon);
|
|
|
|
return icon;
|
|
}
|
|
|
|
WIcon *icon_create_for_dock(WScreen *scr, const char *command, const char *wm_instance, const char *wm_class, int tile)
|
|
{
|
|
WIcon *icon;
|
|
|
|
icon = icon_create_core(scr, 0, 0);
|
|
icon->tile_type = tile;
|
|
|
|
set_icon_image_from_database(icon, wm_instance, wm_class, command);
|
|
/* Update the icon, because icon could be NULL */
|
|
wIconUpdate(icon);
|
|
|
|
WMAddNotificationObserver(appearanceObserver, icon, WNIconAppearanceSettingsChanged, icon);
|
|
WMAddNotificationObserver(tileObserver, icon, WNIconTileSettingsChanged, icon);
|
|
|
|
return icon;
|
|
}
|
|
|
|
static WIcon *icon_create_core(WScreen *scr, int coord_x, int coord_y)
|
|
{
|
|
WIcon *icon;
|
|
|
|
icon = wmalloc(sizeof(WIcon));
|
|
icon->core = wCoreCreateTopLevel(scr,
|
|
coord_x,
|
|
coord_y,
|
|
wPreferences.icon_size,
|
|
wPreferences.icon_size,
|
|
0, scr->w_depth, scr->w_visual, scr->w_colormap,
|
|
scr->white_pixel);
|
|
|
|
/* will be overriden if this is a application icon */
|
|
icon->core->descriptor.handle_mousedown = miniwindowMouseDown;
|
|
icon->core->descriptor.handle_expose = miniwindowExpose;
|
|
icon->core->descriptor.parent_type = WCLASS_MINIWINDOW;
|
|
icon->core->descriptor.parent = icon;
|
|
|
|
icon->core->stacking = wmalloc(sizeof(WStacking));
|
|
icon->core->stacking->above = NULL;
|
|
icon->core->stacking->under = NULL;
|
|
icon->core->stacking->window_level = NORMAL_ICON_LEVEL;
|
|
icon->core->stacking->child_of = NULL;
|
|
|
|
/* Icon image */
|
|
icon->file = NULL;
|
|
icon->file_image = NULL;
|
|
|
|
return icon;
|
|
}
|
|
|
|
void wIconDestroy(WIcon *icon)
|
|
{
|
|
WCoreWindow *core = icon->core;
|
|
WScreen *scr = core->screen_ptr;
|
|
|
|
WMRemoveNotificationObserver(icon);
|
|
|
|
if (icon->handlerID)
|
|
WMDeleteTimerHandler(icon->handlerID);
|
|
|
|
if (icon->icon_win) {
|
|
int x = 0, y = 0;
|
|
|
|
if (icon->owner) {
|
|
x = icon->owner->icon_x;
|
|
y = icon->owner->icon_y;
|
|
}
|
|
wm_backend->window_unmap((WNativeWindow)icon->icon_win);
|
|
wm_backend->client_reparent((WNativeWindow)icon->icon_win, (WNativeWindow)scr->root_win, x, y);
|
|
}
|
|
if (icon->icon_name)
|
|
wfree(icon->icon_name);
|
|
|
|
if (icon->pixmap)
|
|
wm_backend->pixmap_free((unsigned long)icon->pixmap);
|
|
|
|
if (icon->mini_preview)
|
|
wm_backend->pixmap_free((unsigned long)icon->mini_preview);
|
|
|
|
unset_icon_image(icon);
|
|
|
|
wCoreDestroy(icon->core);
|
|
wfree(icon);
|
|
}
|
|
|
|
static void drawIconTitleBackground(WScreen *scr, WMNativePixmap pixmap, int height)
|
|
{
|
|
wm_backend->icon_paint_title_bg(scr, (unsigned long)pixmap, height);
|
|
}
|
|
|
|
static void icon_update_pixmap(WIcon *icon, RImage *image)
|
|
{
|
|
RImage *tile;
|
|
WMNativePixmap pixmap;
|
|
int x, y, sx, sy;
|
|
unsigned w, h;
|
|
int theight = 0;
|
|
WScreen *scr = icon->core->screen_ptr;
|
|
|
|
switch (icon->tile_type) {
|
|
case TILE_NORMAL:
|
|
tile = RCloneImage(scr->icon_tile);
|
|
break;
|
|
case TILE_CLIP:
|
|
tile = RCloneImage(scr->clip_tile);
|
|
break;
|
|
case TILE_DRAWER:
|
|
tile = RCloneImage(scr->drawer_tile);
|
|
break;
|
|
default:
|
|
/*
|
|
* The icon has always rigth value, this case is
|
|
* only to avoid a compiler warning with "tile"
|
|
* "may be used uninitialized"
|
|
*/
|
|
wwarning("Unknown tile type: %d.\n", icon->tile_type);
|
|
tile = RCloneImage(scr->icon_tile);
|
|
}
|
|
|
|
if (image) {
|
|
w = (image->width > wPreferences.icon_size)
|
|
? wPreferences.icon_size : image->width;
|
|
x = (wPreferences.icon_size - w) / 2;
|
|
sx = (image->width - w) / 2;
|
|
|
|
if (icon->show_title)
|
|
theight = WMFontHeight(scr->icon_title_font);
|
|
|
|
h = (image->height + theight > wPreferences.icon_size
|
|
? wPreferences.icon_size - theight : image->height);
|
|
y = theight + (wPreferences.icon_size - theight - h) / 2;
|
|
sy = (image->height - h) / 2;
|
|
|
|
RCombineArea(tile, image, sx, sy, w, h, x, y);
|
|
}
|
|
|
|
if (icon->shadowed) {
|
|
RColor color;
|
|
|
|
color.red = scr->icon_back_texture->light.red >> 8;
|
|
color.green = scr->icon_back_texture->light.green >> 8;
|
|
color.blue = scr->icon_back_texture->light.blue >> 8;
|
|
color.alpha = 150; /* about 60% */
|
|
RClearImage(tile, &color);
|
|
}
|
|
|
|
if (icon->highlighted) {
|
|
RColor color;
|
|
|
|
color.red = color.green = color.blue = 0;
|
|
color.alpha = 160;
|
|
RLightImage(tile, &color);
|
|
}
|
|
|
|
if (!RConvertImage(scr->rcontext, tile, &pixmap))
|
|
wwarning(_("error rendering image:%s"), RMessageForError(RErrorCode));
|
|
|
|
RReleaseImage(tile);
|
|
|
|
/* Draw the icon's title background (without text) */
|
|
if (icon->show_title)
|
|
drawIconTitleBackground(scr, pixmap, theight);
|
|
|
|
icon->pixmap = pixmap;
|
|
}
|
|
|
|
void wIconChangeTitle(WIcon *icon, WWindow *wwin)
|
|
{
|
|
if (!icon || !wwin)
|
|
return;
|
|
|
|
/* Remove the previous icon title */
|
|
if (icon->icon_name != NULL)
|
|
wfree(icon->icon_name);
|
|
|
|
/* Set the new one through the backend. X11 backend tries
|
|
* EWMH _NET_WM_ICON_NAME first, falls back to ICCCM WM_ICON_NAME,
|
|
* then to WM_NAME / _NET_WM_NAME. Wayland backend returns the
|
|
* xdg_toplevel.title (which is what XWayland clients see too). */
|
|
icon->icon_name = NULL;
|
|
if (!wm_backend->window_get_icon_name(wwin->client_win, &icon->icon_name) ||
|
|
!icon->icon_name) {
|
|
wm_backend->window_fetch_name(wwin->client_win, &icon->icon_name);
|
|
}
|
|
}
|
|
|
|
RImage *wIconValidateIconSize(RImage *icon, int max_size)
|
|
{
|
|
RImage *nimage;
|
|
|
|
if (!icon)
|
|
return NULL;
|
|
|
|
/* We should hold "ICON_BORDER" (~2) pixels to include the icon border */
|
|
if (((max_size + ICON_BORDER) < icon->width) ||
|
|
((max_size + ICON_BORDER) < icon->height)) {
|
|
if (icon->width > icon->height)
|
|
nimage = RScaleImage(icon, max_size - ICON_BORDER,
|
|
(icon->height * (max_size - ICON_BORDER) / icon->width));
|
|
else
|
|
nimage = RScaleImage(icon, (icon->width * (max_size - ICON_BORDER) / icon->height),
|
|
max_size - ICON_BORDER);
|
|
RReleaseImage(icon);
|
|
icon = nimage;
|
|
}
|
|
|
|
return icon;
|
|
}
|
|
|
|
int wIconChangeImageFile(WIcon *icon, const char *file)
|
|
{
|
|
WScreen *scr = icon->core->screen_ptr;
|
|
char *path;
|
|
RImage *image = NULL;
|
|
|
|
/* If no new image, don't do nothing */
|
|
if (!file)
|
|
return 1;
|
|
|
|
/* Find the new image */
|
|
path = FindImage(wPreferences.icon_path, file);
|
|
if (!path)
|
|
return 0;
|
|
|
|
image = get_rimage_from_file(scr, path, wPreferences.icon_size);
|
|
if (!image) {
|
|
wfree(path);
|
|
return 0;
|
|
}
|
|
|
|
/* Set the new image */
|
|
set_icon_image_from_image(icon, image);
|
|
icon->file = wstrdup(path);
|
|
update_icon_pixmap(icon);
|
|
|
|
wfree(path);
|
|
return 1;
|
|
}
|
|
|
|
static char *get_name_for_wwin(WWindow *wwin)
|
|
{
|
|
return get_name_for_instance_class(wwin->wm_instance, wwin->wm_class);
|
|
}
|
|
|
|
char *get_name_for_instance_class(const char *wm_instance, const char *wm_class)
|
|
{
|
|
char *suffix;
|
|
int len;
|
|
|
|
if (wm_class && wm_instance) {
|
|
len = strlen(wm_class) + strlen(wm_instance) + 2;
|
|
suffix = wmalloc(len);
|
|
snprintf(suffix, len, "%s.%s", wm_instance, wm_class);
|
|
} else if (wm_class) {
|
|
len = strlen(wm_class) + 1;
|
|
suffix = wmalloc(len);
|
|
snprintf(suffix, len, "%s", wm_class);
|
|
} else if (wm_instance) {
|
|
len = strlen(wm_instance) + 1;
|
|
suffix = wmalloc(len);
|
|
snprintf(suffix, len, "%s", wm_instance);
|
|
} else {
|
|
return NULL;
|
|
}
|
|
|
|
return suffix;
|
|
}
|
|
|
|
static char *get_icon_cache_path(void)
|
|
{
|
|
const char *prefix;
|
|
char *path;
|
|
int ret;
|
|
|
|
prefix = wuserdatapath();
|
|
path = wstrconcat(prefix, CACHE_ICON_PATH "/");
|
|
|
|
/* If the folder exists, exit */
|
|
if (access(path, F_OK) == 0)
|
|
return path;
|
|
|
|
/* Create the folder */
|
|
ret = wmkdirhier(path);
|
|
|
|
/* Exit 1 on success, 0 on failure */
|
|
if (ret == 1)
|
|
return path;
|
|
|
|
/* Fail */
|
|
wfree(path);
|
|
return NULL;
|
|
}
|
|
|
|
static RImage *get_wwindow_image_from_wmhints(WWindow *wwin, WIcon *icon)
|
|
{
|
|
RImage *image = NULL;
|
|
WWMHints *hints = wwin->wm_hints;
|
|
|
|
if (hints && (hints->flags & WMH_ICON_PIXMAP_HINT) && hints->icon_pixmap != WNATIVE_NONE)
|
|
image = RCreateImageFromDrawable(icon->core->screen_ptr->rcontext,
|
|
(WMDrawable)hints->icon_pixmap,
|
|
(hints->flags & WMH_ICON_MASK_HINT)
|
|
? (WMDrawable)hints->icon_mask : WM_NATIVE_NONE);
|
|
|
|
return image;
|
|
}
|
|
|
|
/*
|
|
* wIconStore--
|
|
* Stores the client supplied icon at CACHE_ICON_PATH
|
|
* and returns the path for that icon. Returns NULL if there is no
|
|
* client supplied icon or on failure.
|
|
*
|
|
* Side effects:
|
|
* New directories might be created.
|
|
*/
|
|
char *wIconStore(WIcon *icon)
|
|
{
|
|
char *path, *dir_path, *file, *filename;
|
|
int len = 0;
|
|
RImage *image = NULL;
|
|
WWindow *wwin = icon->owner;
|
|
|
|
if (!wwin)
|
|
return NULL;
|
|
|
|
dir_path = get_icon_cache_path();
|
|
if (!dir_path)
|
|
return NULL;
|
|
|
|
file = get_name_for_wwin(wwin);
|
|
if (!file) {
|
|
wfree(dir_path);
|
|
return NULL;
|
|
}
|
|
|
|
/* Create the file name */
|
|
len = strlen(file) + 5;
|
|
filename = wmalloc(len);
|
|
snprintf(filename, len, "%s.xpm", file);
|
|
wfree(file);
|
|
|
|
/* Create the full path, including the filename */
|
|
len = strlen(dir_path) + strlen(filename) + 1;
|
|
path = wmalloc(len);
|
|
snprintf(path, len, "%s%s", dir_path, filename);
|
|
wfree(dir_path);
|
|
|
|
/* If icon exists, exit */
|
|
if (access(path, F_OK) == 0) {
|
|
wfree(path);
|
|
return filename;
|
|
}
|
|
|
|
if (wwin->net_icon_image)
|
|
image = RRetainImage(wwin->net_icon_image);
|
|
else
|
|
image = get_wwindow_image_from_wmhints(wwin, icon);
|
|
|
|
if (!image) {
|
|
wfree(path);
|
|
wfree(filename);
|
|
return NULL;
|
|
}
|
|
|
|
if (!RSaveImage(image, path, "XPM")) {
|
|
wfree(filename);
|
|
filename = NULL;
|
|
}
|
|
|
|
wfree(path);
|
|
RReleaseImage(image);
|
|
|
|
return filename;
|
|
}
|
|
|
|
void remove_cache_icon(char *filename)
|
|
{
|
|
char *cachepath;
|
|
|
|
if (!filename)
|
|
return;
|
|
|
|
cachepath = get_icon_cache_path();
|
|
if (!cachepath)
|
|
return;
|
|
|
|
/* Filename check/parse could be here */
|
|
if (!strncmp(filename, cachepath, strlen(cachepath)))
|
|
unlink(filename);
|
|
|
|
wfree(cachepath);
|
|
}
|
|
|
|
static void cycleColor(void *data)
|
|
{
|
|
WIcon *icon = (WIcon *) data;
|
|
WScreen *scr = icon->core->screen_ptr;
|
|
|
|
icon->step--;
|
|
wm_backend->icon_select_paint(icon, icon->step);
|
|
wm_backend->icon_select_paint(icon, 0);
|
|
icon->handlerID = WMAddTimerHandler(COLOR_CYCLE_DELAY, cycleColor, icon);
|
|
}
|
|
|
|
void wIconSetHighlited(WIcon *icon, Bool flag)
|
|
{
|
|
if (icon->highlighted == flag)
|
|
return;
|
|
|
|
icon->highlighted = flag;
|
|
update_icon_pixmap(icon);
|
|
}
|
|
|
|
void wIconSelect(WIcon *icon)
|
|
{
|
|
WScreen *scr = icon->core->screen_ptr;
|
|
icon->selected = !icon->selected;
|
|
|
|
if (icon->selected) {
|
|
icon->step = 0;
|
|
if (!wPreferences.dont_blink)
|
|
icon->handlerID = WMAddTimerHandler(10, cycleColor, icon);
|
|
else
|
|
wm_backend->icon_select_paint(icon, 0);
|
|
|
|
} else {
|
|
if (icon->handlerID) {
|
|
WMDeleteTimerHandler(icon->handlerID);
|
|
icon->handlerID = NULL;
|
|
}
|
|
wm_backend->window_clear(icon->core->window);
|
|
}
|
|
}
|
|
|
|
static void unset_icon_image(WIcon *icon)
|
|
{
|
|
if (icon->file) {
|
|
wfree(icon->file);
|
|
icon->file = NULL;
|
|
}
|
|
|
|
if (icon->file_image) {
|
|
RReleaseImage(icon->file_image);
|
|
icon->file_image = NULL;
|
|
}
|
|
}
|
|
|
|
void set_icon_image_from_image(WIcon *icon, RImage *image)
|
|
{
|
|
if (!icon)
|
|
return;
|
|
|
|
unset_icon_image(icon);
|
|
|
|
icon->file_image = NULL;
|
|
icon->file_image = image;
|
|
}
|
|
|
|
void set_icon_minipreview(WIcon *icon, RImage *image)
|
|
{
|
|
WMNativePixmap tmp;
|
|
RImage *scaled_mini_preview;
|
|
WScreen *scr = icon->core->screen_ptr;
|
|
|
|
scaled_mini_preview = RSmoothScaleImage(image, wPreferences.minipreview_size - 2 * MINIPREVIEW_BORDER,
|
|
wPreferences.minipreview_size - 2 * MINIPREVIEW_BORDER);
|
|
|
|
if (RConvertImage(scr->rcontext, scaled_mini_preview, &tmp)) {
|
|
if (icon->mini_preview != WM_NATIVE_NONE)
|
|
wm_backend->pixmap_free((unsigned long)icon->mini_preview);
|
|
icon->mini_preview = tmp;
|
|
}
|
|
RReleaseImage(scaled_mini_preview);
|
|
}
|
|
|
|
void wIconUpdate(WIcon *icon)
|
|
{
|
|
WWindow *wwin = NULL;
|
|
|
|
if (icon && icon->owner)
|
|
wwin = icon->owner;
|
|
|
|
if (wwin && WFLAGP(wwin, always_user_icon)) {
|
|
/* Forced use user_icon */
|
|
get_rimage_icon_from_user_icon(icon);
|
|
} else if (icon->icon_win != WM_NATIVE_NONE) {
|
|
/* Get the WMNativePixmap from the WIcon */
|
|
get_rimage_icon_from_icon_win(icon);
|
|
} else if (wwin && wwin->net_icon_image) {
|
|
/* Use _NET_WM_ICON icon */
|
|
get_rimage_icon_from_x11(icon);
|
|
} else if (wwin && wwin->wm_hints && (wwin->wm_hints->flags & WMH_ICON_PIXMAP_HINT)) {
|
|
/* Get the WMNativePixmap from the wm_hints, else, from the user */
|
|
unset_icon_image(icon);
|
|
icon->file_image = get_rimage_icon_from_wm_hints(icon);
|
|
if (!icon->file_image)
|
|
get_rimage_icon_from_user_icon(icon);
|
|
} else {
|
|
/* Get the WMNativePixmap from the user */
|
|
get_rimage_icon_from_user_icon(icon);
|
|
}
|
|
|
|
update_icon_pixmap(icon);
|
|
}
|
|
|
|
void update_icon_pixmap(WIcon *icon)
|
|
{
|
|
|
|
if (icon->pixmap != WM_NATIVE_NONE)
|
|
wm_backend->pixmap_free((unsigned long)icon->pixmap);
|
|
|
|
icon->pixmap = WM_NATIVE_NONE;
|
|
|
|
/* Create the pixmap */
|
|
if (icon->file_image)
|
|
icon_update_pixmap(icon, icon->file_image);
|
|
|
|
/* If dockapp, put inside the icon */
|
|
if (icon->icon_win != WM_NATIVE_NONE) {
|
|
/* file_image is NULL, because is docked app */
|
|
icon_update_pixmap(icon, NULL);
|
|
set_dockapp_in_icon(icon);
|
|
}
|
|
|
|
/* No pixmap, set default background */
|
|
if (icon->pixmap != WM_NATIVE_NONE)
|
|
wm_backend->window_set_background_pixmap((WNativeWindow)icon->core->window, (unsigned long)icon->pixmap);
|
|
|
|
/* Paint it */
|
|
wIconPaint(icon);
|
|
}
|
|
|
|
static void get_rimage_icon_from_x11(WIcon *icon)
|
|
{
|
|
/* Remove the icon image */
|
|
unset_icon_image(icon);
|
|
|
|
/* Set the new icon image */
|
|
icon->file_image = RRetainImage(icon->owner->net_icon_image);
|
|
}
|
|
|
|
static void get_rimage_icon_from_user_icon(WIcon *icon)
|
|
{
|
|
if (icon->file_image)
|
|
return;
|
|
|
|
get_rimage_icon_from_default_icon(icon);
|
|
}
|
|
|
|
static void get_rimage_icon_from_default_icon(WIcon *icon)
|
|
{
|
|
WScreen *scr = icon->core->screen_ptr;
|
|
|
|
/* If the icon don't have image, we should use the default image. */
|
|
if (!scr->def_icon_rimage)
|
|
scr->def_icon_rimage = get_default_image(scr);
|
|
|
|
/* Remove the icon image */
|
|
unset_icon_image(icon);
|
|
|
|
/* Set the new icon image */
|
|
icon->file_image = RRetainImage(scr->def_icon_rimage);
|
|
}
|
|
|
|
/* Get the RImage from the WIcon of the WWindow */
|
|
static void get_rimage_icon_from_icon_win(WIcon *icon)
|
|
{
|
|
RImage *image;
|
|
|
|
/* Create the new RImage via backend (X11: parses _NET_WM_ICON via dpy;
|
|
* Wayland: parses _NET_WM_ICON via wl_state.x_display for XWayland). */
|
|
image = (RImage *)wm_backend->window_get_icon_image(icon->icon_win);
|
|
|
|
/* Free the icon info */
|
|
unset_icon_image(icon);
|
|
|
|
/* Set the new info */
|
|
icon->file_image = image;
|
|
}
|
|
|
|
/* Set the dockapp in the WIcon */
|
|
static void set_dockapp_in_icon(WIcon *icon)
|
|
{
|
|
WScreen *scr = icon->core->screen_ptr;
|
|
unsigned int w, h, d;
|
|
|
|
/* Reparent the dock application to the icon */
|
|
|
|
/* We need the application size to center it
|
|
* and show in the correct position */
|
|
getSize(icon->icon_win, &w, &h, &d);
|
|
|
|
/* Set the background pixmap */
|
|
wm_backend->window_set_background_pixmap((WNativeWindow)icon->core->window, (unsigned long)scr->icon_tile_pixmap);
|
|
|
|
/* Set the icon border */
|
|
wm_backend->window_set_border_width((WNativeWindow)icon->icon_win, 0);
|
|
|
|
/* Put the dock application in the icon */
|
|
wm_backend->client_reparent((WNativeWindow)icon->icon_win, icon->core->window,
|
|
(wPreferences.icon_size - w) / 2,
|
|
(wPreferences.icon_size - h) / 2);
|
|
|
|
/* Show it and save */
|
|
wm_backend->window_map((WNativeWindow)icon->icon_win);
|
|
wm_backend->client_add_to_save_set((WNativeWindow)icon->icon_win);
|
|
|
|
/* Needed to move the icon clicking on the application part */
|
|
if (wm_backend->window_get_event_mask((WNativeWindow)icon->icon_win) &&
|
|
WM_EVMASK_BUTTON_PRESS)
|
|
wHackedGrabButton(Button1, MOD_MASK, icon->core->window, True,
|
|
WM_EVMASK_BUTTON_PRESS, WM_GRAB_MODE_SYNC, WM_GRAB_MODE_ASYNC,
|
|
WM_NATIVE_NONE, wPreferences.cursor[WCUR_ARROW]);
|
|
}
|
|
|
|
/* Get the RImage from the XWindow wm_hints */
|
|
RImage *get_rimage_icon_from_wm_hints(WIcon *icon)
|
|
{
|
|
RImage *image = NULL;
|
|
unsigned int w, h, d;
|
|
WWindow *wwin;
|
|
|
|
if ((!icon) || (!icon->owner))
|
|
return NULL;
|
|
|
|
wwin = icon->owner;
|
|
|
|
if (!getSize((WMDrawable)wwin->wm_hints->icon_pixmap, &w, &h, &d)) {
|
|
icon->owner->wm_hints->flags &= ~WMH_ICON_PIXMAP_HINT;
|
|
return NULL;
|
|
}
|
|
|
|
image = get_wwindow_image_from_wmhints(wwin, icon);
|
|
if (!image)
|
|
return NULL;
|
|
|
|
/* Resize the icon to the wPreferences.icon_size size */
|
|
image = wIconValidateIconSize(image, wPreferences.icon_size);
|
|
|
|
return image;
|
|
}
|
|
|
|
/* This function updates in the screen the icon title */
|
|
static void update_icon_title(WIcon *icon)
|
|
{
|
|
WScreen *scr = icon->core->screen_ptr;
|
|
int x, l, w;
|
|
char *tmp;
|
|
|
|
/* draw the icon title */
|
|
if (icon->show_title && icon->icon_name != NULL) {
|
|
tmp = ShrinkString(scr->icon_title_font, icon->icon_name, wPreferences.icon_size - 4);
|
|
w = WMWidthOfString(scr->icon_title_font, tmp, l = strlen(tmp));
|
|
|
|
if (w > icon->core->width - 4)
|
|
x = (icon->core->width - 4) - w;
|
|
else
|
|
x = (icon->core->width - w) / 2;
|
|
|
|
WMDrawString(scr->wmscreen, icon->core->window, scr->icon_title_color,
|
|
scr->icon_title_font, x, 1, tmp, l);
|
|
wfree(tmp);
|
|
}
|
|
}
|
|
|
|
|
|
void wIconPaint(WIcon *icon)
|
|
{
|
|
if (!icon || !icon->core || !icon->core->screen_ptr)
|
|
return;
|
|
|
|
if (!icon->core->window)
|
|
return;
|
|
|
|
WScreen *scr = icon->core->screen_ptr;
|
|
|
|
wm_backend->window_clear((WNativeWindow)icon->core->window);
|
|
|
|
update_icon_title(icon);
|
|
|
|
if (icon->selected)
|
|
wm_backend->icon_select_paint(icon, 0);
|
|
|
|
/* Allow the backend to composite the full icon (tile + image + title)
|
|
* and commit it to its rendering surface. */
|
|
wm_backend->icon_paint(icon);
|
|
}
|
|
|
|
/******************************************************************/
|
|
|
|
static void miniwindowExpose(WObjDescriptor *desc, WBackendEvent *event)
|
|
{
|
|
/* Parameter not used, but tell the compiler that it is ok */
|
|
(void) event;
|
|
|
|
wIconPaint(desc->parent);
|
|
}
|
|
|
|
static void miniwindowDblClick(WObjDescriptor *desc, WBackendEvent *event)
|
|
{
|
|
WIcon *icon = desc->parent;
|
|
|
|
/* Parameter not used, but tell the compiler that it is ok */
|
|
(void) event;
|
|
|
|
assert(icon->owner != NULL);
|
|
|
|
wDeiconifyWindow(icon->owner);
|
|
}
|
|
|
|
static void miniwindowMouseDown(WObjDescriptor *desc, WBackendEvent *event)
|
|
{
|
|
WIcon *icon = desc->parent;
|
|
WWindow *wwin = icon->owner;
|
|
WMEvent ev;
|
|
int x = wwin->icon_x, y = wwin->icon_y;
|
|
int dx = event->u.button.x, dy = event->u.button.y;
|
|
int grabbed = 0;
|
|
int clickButton = event->u.button.button;
|
|
Bool hasMoved = False;
|
|
|
|
if (WCHECK_STATE(WSTATE_MODAL))
|
|
return;
|
|
|
|
if (IsDoubleClick(icon->core->screen_ptr, event)) {
|
|
miniwindowDblClick(desc, event);
|
|
return;
|
|
}
|
|
|
|
if (event->u.button.button == Button1) {
|
|
if (event->u.button.state & MOD_MASK)
|
|
wLowerFrame(icon->core);
|
|
else
|
|
wRaiseFrame(icon->core);
|
|
if (event->u.button.state & WM_MOD_SHIFT) {
|
|
wIconSelect(icon);
|
|
wSelectWindow(icon->owner, !wwin->flags.selected);
|
|
}
|
|
} else if (event->u.button.button == Button3) {
|
|
WObjDescriptor *desc;
|
|
|
|
OpenMiniwindowMenu(wwin, event->u.button.x_root, event->u.button.y_root);
|
|
|
|
/* allow drag select of menu */
|
|
desc = &wwin->screen_ptr->window_menu->menu->descriptor;
|
|
event->u.button.send_event = True;
|
|
(*desc->handle_mousedown) (desc, event);
|
|
|
|
return;
|
|
}
|
|
|
|
wm_backend->pointer_grab((WNativeWindow)icon->core->window, WNATIVE_NONE,
|
|
WM_EVMASK_BUTTON_MOTION | WM_EVMASK_BUTTON_RELEASE | WM_EVMASK_BUTTON_PRESS,
|
|
WCUR_NORMAL, False, 0);
|
|
while (1) {
|
|
WMMaskEvent(wwin->screen_ptr->wmscreen, WM_EVMASK_POINTER_MOTION | WM_EVMASK_BUTTON_RELEASE | WM_EVMASK_BUTTON_PRESS
|
|
| WM_EVMASK_BUTTON_MOTION | WM_EVMASK_EXPOSURE, &ev);
|
|
switch (ev.type) {
|
|
case WME_EXPOSE:
|
|
WMHandleEvent(&ev);
|
|
break;
|
|
|
|
case WME_MOTION:
|
|
hasMoved = True;
|
|
if (!grabbed) {
|
|
if (abs(dx - ev.u.motion.x) >= MOVE_THRESHOLD
|
|
|| abs(dy - ev.u.motion.y) >= MOVE_THRESHOLD) {
|
|
wm_backend->pointer_change_cursor(WCUR_MOVE);
|
|
grabbed = 1;
|
|
} else {
|
|
break;
|
|
}
|
|
}
|
|
x = ev.u.motion.x_root - dx;
|
|
y = ev.u.motion.y_root - dy;
|
|
wm_backend->window_move((WNativeWindow)icon->core->window, x, y);
|
|
break;
|
|
|
|
case WME_BUTTON_PRESS:
|
|
break;
|
|
|
|
case WME_BUTTON_RELEASE:
|
|
if (ev.u.button.button != clickButton)
|
|
break;
|
|
|
|
if (wwin->icon_x != x || wwin->icon_y != y)
|
|
wwin->flags.icon_moved = 1;
|
|
|
|
wm_backend->window_move((WNativeWindow)icon->core->window, x, y);
|
|
|
|
wwin->icon_x = x;
|
|
wwin->icon_y = y;
|
|
wm_backend->pointer_ungrab(0);
|
|
|
|
if (wPreferences.auto_arrange_icons)
|
|
wArrangeIcons(wwin->screen_ptr, True);
|
|
if (wPreferences.single_click && !hasMoved)
|
|
miniwindowDblClick(desc, event);
|
|
return;
|
|
|
|
}
|
|
}
|
|
}
|
|
|
|
void set_icon_image_from_database(WIcon *icon, const char *wm_instance, const char *wm_class, const char *command)
|
|
{
|
|
char *file = NULL;
|
|
|
|
file = get_icon_filename(wm_instance, wm_class, command, False);
|
|
if (file) {
|
|
icon->file = wstrdup(file);
|
|
icon->file_image = get_rimage_from_file(icon->core->screen_ptr, icon->file, wPreferences.icon_size);
|
|
wfree(file);
|
|
}
|
|
}
|