we support _NET_WM_ICON

2001-08-19  Havoc Pennington  <hp@pobox.com>

	* src/screen.c (set_supported_hint): we support _NET_WM_ICON

	* src/wm-tester/main.c: add stuff to test _NET_WM_ICON
	(but it doesn't work, so it isn't tested yet)

	* src/window.c (update_icon): read _NET_WM_ICON

	* src/screen.c (meta_screen_new): set the WM_ICON_SIZE hint

	* src/tabpopup.c (meta_ui_tab_popup_select): remove assertion

	* src/window.c (meta_window_get_icon_geometry): fix obscure
	memleak
This commit is contained in:
Havoc Pennington 2001-08-20 01:42:44 +00:00 committed by Havoc Pennington
parent 78a68f3e10
commit 04e09d4c56
12 changed files with 460 additions and 30 deletions

View File

@ -1,3 +1,19 @@
2001-08-19 Havoc Pennington <hp@pobox.com>
* src/screen.c (set_supported_hint): we support _NET_WM_ICON
* src/wm-tester/main.c: add stuff to test _NET_WM_ICON
(but it doesn't work, so it isn't tested yet)
* src/window.c (update_icon): read _NET_WM_ICON
* src/screen.c (meta_screen_new): set the WM_ICON_SIZE hint
* src/tabpopup.c (meta_ui_tab_popup_select): remove assertion
* src/window.c (meta_window_get_icon_geometry): fix obscure
memleak
2001-08-19 Havoc Pennington <hp@pobox.com> 2001-08-19 Havoc Pennington <hp@pobox.com>
* src/display.c (meta_display_grab_window_buttons): remove XSync, * src/display.c (meta_display_grab_window_buttons): remove XSync,

View File

@ -132,7 +132,8 @@ meta_display_open (const char *name)
"_NET_WM_ICON_NAME", "_NET_WM_ICON_NAME",
"_NET_WM_ICON", "_NET_WM_ICON",
"_NET_WM_ICON_GEOMETRY", "_NET_WM_ICON_GEOMETRY",
"UTF8_STRING" "UTF8_STRING",
"WM_ICON_SIZE"
}; };
Atom atoms[G_N_ELEMENTS(atom_names)]; Atom atoms[G_N_ELEMENTS(atom_names)];
@ -216,6 +217,7 @@ meta_display_open (const char *name)
display->atom_net_wm_icon = atoms[37]; display->atom_net_wm_icon = atoms[37];
display->atom_net_wm_icon_geometry = atoms[38]; display->atom_net_wm_icon_geometry = atoms[38];
display->atom_utf8_string = atoms[39]; display->atom_utf8_string = atoms[39];
display->atom_wm_icon_size = atoms[40];
/* Offscreen unmapped window used for _NET_SUPPORTING_WM_CHECK, /* Offscreen unmapped window used for _NET_SUPPORTING_WM_CHECK,
* created in screen_new * created in screen_new

View File

@ -97,6 +97,7 @@ struct _MetaDisplay
Atom atom_net_wm_icon; Atom atom_net_wm_icon;
Atom atom_net_wm_icon_geometry; Atom atom_net_wm_icon_geometry;
Atom atom_utf8_string; Atom atom_utf8_string;
Atom atom_wm_icon_size;
/* This is the actual window from focus events, /* This is the actual window from focus events,
* not the one we last set * not the one we last set

View File

@ -22,6 +22,10 @@ if test -n "$EVIL_TEST"; then
TEST_CLIENT='./wm-tester/wm-tester --evil' TEST_CLIENT='./wm-tester/wm-tester --evil'
fi fi
if test -n "$ICON_TEST"; then
TEST_CLIENT='./wm-tester/wm-tester --icon-windows'
fi
if test -z "$ONLY_WM"; then if test -z "$ONLY_WM"; then
Xnest -ac :1 -scrns $SCREENS -geometry 640x480 -bw 15 & Xnest -ac :1 -scrns $SCREENS -geometry 640x480 -bw 15 &
## usleep 800000 ## usleep 800000

View File

@ -72,7 +72,7 @@ set_wm_check_hint (MetaScreen *screen)
static int static int
set_supported_hint (MetaScreen *screen) set_supported_hint (MetaScreen *screen)
{ {
#define N_SUPPORTED 21 #define N_SUPPORTED 22
#define N_WIN_SUPPORTED 1 #define N_WIN_SUPPORTED 1
Atom atoms[N_SUPPORTED]; Atom atoms[N_SUPPORTED];
@ -97,6 +97,7 @@ set_supported_hint (MetaScreen *screen)
atoms[18] = screen->display->atom_net_client_list_stacking; atoms[18] = screen->display->atom_net_client_list_stacking;
atoms[19] = screen->display->atom_net_wm_state_skip_taskbar; atoms[19] = screen->display->atom_net_wm_state_skip_taskbar;
atoms[20] = screen->display->atom_net_wm_state_skip_pager; atoms[20] = screen->display->atom_net_wm_state_skip_pager;
atoms[21] = screen->display->atom_net_wm_icon;
XChangeProperty (screen->display->xdisplay, screen->xroot, XChangeProperty (screen->display->xdisplay, screen->xroot,
screen->display->atom_net_wm_supported, screen->display->atom_net_wm_supported,
@ -115,6 +116,29 @@ set_supported_hint (MetaScreen *screen)
#undef N_SUPPORTED #undef N_SUPPORTED
} }
static int
set_wm_icon_size_hint (MetaScreen *screen)
{
#define N_VALS 6
gulong vals[N_VALS];
/* min width, min height, max w, max h, width inc, height inc */
vals[0] = META_ICON_WIDTH;
vals[1] = META_ICON_HEIGHT;
vals[2] = META_ICON_WIDTH;
vals[3] = META_ICON_HEIGHT;
vals[4] = 0;
vals[5] = 0;
XChangeProperty (screen->display->xdisplay, screen->xroot,
screen->display->atom_wm_icon_size,
XA_CARDINAL,
32, PropModeReplace, (guchar*) vals, N_VALS);
return Success;
#undef N_VALS
}
MetaScreen* MetaScreen*
meta_screen_new (MetaDisplay *display, meta_screen_new (MetaDisplay *display,
int number) int number)
@ -178,6 +202,8 @@ meta_screen_new (MetaDisplay *display,
screen->xroot, screen->xroot,
-100, -100, 1, 1, 0, 0, 0); -100, -100, 1, 1, 0, 0, 0);
set_wm_icon_size_hint (screen);
set_supported_hint (screen); set_supported_hint (screen);
set_wm_check_hint (screen); set_wm_check_hint (screen);

View File

@ -26,6 +26,11 @@
#include <X11/Xutil.h> #include <X11/Xutil.h>
#include "ui.h" #include "ui.h"
/* should investigate changing these to whatever most apps use */
#define META_ICON_WIDTH 32
#define META_ICON_HEIGHT 32
typedef void (* MetaScreenWindowFunc) (MetaScreen *screen, MetaWindow *window, typedef void (* MetaScreenWindowFunc) (MetaScreen *screen, MetaWindow *window,
gpointer user_data); gpointer user_data);

View File

@ -287,6 +287,4 @@ meta_ui_tab_popup_select (MetaTabPopup *popup,
tmp = tmp->next; tmp = tmp->next;
} }
meta_bug ("Selected nonexistent entry 0x%lx in tab popup\n", xwindow);
} }

View File

@ -333,6 +333,41 @@ meta_gdk_pixbuf_get_from_window (GdkPixbuf *dest,
return retval; return retval;
} }
GdkPixbuf*
meta_gdk_pixbuf_get_from_pixmap (GdkPixbuf *dest,
Pixmap xpixmap,
int src_x,
int src_y,
int dest_x,
int dest_y,
int width,
int height)
{
GdkDrawable *drawable;
GdkPixbuf *retval;
retval = NULL;
drawable = gdk_xid_table_lookup (xpixmap);
if (drawable)
g_object_ref (G_OBJECT (drawable));
else
drawable = gdk_pixmap_foreign_new (xpixmap);
retval = gdk_pixbuf_get_from_drawable (dest,
drawable,
/* We assume root window cmap */
gdk_colormap_get_system (),
src_x, src_y,
dest_x, dest_y,
width, height);
g_object_unref (G_OBJECT (drawable));
return retval;
}
void void
meta_ui_push_delay_exposes (MetaUI *ui) meta_ui_push_delay_exposes (MetaUI *ui)
{ {

View File

@ -110,6 +110,15 @@ GdkPixbuf* meta_gdk_pixbuf_get_from_window (GdkPixbuf *dest,
int width, int width,
int height); int height);
GdkPixbuf* meta_gdk_pixbuf_get_from_pixmap (GdkPixbuf *dest,
Pixmap xpixmap,
int src_x,
int src_y,
int dest_x,
int dest_y,
int width,
int height);
/* Used when we have a server grab and draw all over everything, /* Used when we have a server grab and draw all over everything,
* then we need to handle exposes after doing that, instead of * then we need to handle exposes after doing that, instead of
* during it * during it

View File

@ -59,7 +59,8 @@ static int update_role (MetaWindow *window);
static int update_net_wm_type (MetaWindow *window); static int update_net_wm_type (MetaWindow *window);
static int update_initial_workspace (MetaWindow *window); static int update_initial_workspace (MetaWindow *window);
static int update_icon_name (MetaWindow *window); static int update_icon_name (MetaWindow *window);
static int update_icon (MetaWindow *window); static int update_icon (MetaWindow *window,
gboolean reread_rgb_icon);
static void recalc_window_type (MetaWindow *window); static void recalc_window_type (MetaWindow *window);
static void recalc_window_features (MetaWindow *window); static void recalc_window_features (MetaWindow *window);
static int set_wm_state (MetaWindow *window, static int set_wm_state (MetaWindow *window,
@ -318,6 +319,8 @@ meta_window_new (MetaDisplay *display, Window xwindow,
window->icon_pixmap = None; window->icon_pixmap = None;
window->icon_mask = None; window->icon_mask = None;
window->using_rgb_icon = FALSE;
window->type = META_WINDOW_NORMAL; window->type = META_WINDOW_NORMAL;
window->type_atom = None; window->type_atom = None;
@ -339,7 +342,8 @@ meta_window_new (MetaDisplay *display, Window xwindow,
update_net_wm_type (window); update_net_wm_type (window);
update_initial_workspace (window); update_initial_workspace (window);
update_icon_name (window); update_icon_name (window);
update_icon (window); /* should come after wm_hints */
update_icon (window, TRUE);
if (!window->mapped && if (!window->mapped &&
(window->size_hints.flags & PPosition) == 0 && (window->size_hints.flags & PPosition) == 0 &&
@ -2331,7 +2335,6 @@ process_property_notify (MetaWindow *window,
meta_verbose ("Property notify on %s for WM_HINTS\n", window->desc); meta_verbose ("Property notify on %s for WM_HINTS\n", window->desc);
update_wm_hints (window); update_wm_hints (window);
update_icon (window);
meta_window_queue_move_resize (window); meta_window_queue_move_resize (window);
} }
@ -2395,7 +2398,7 @@ process_property_notify (MetaWindow *window,
else if (event->atom == window->display->atom_net_wm_icon) else if (event->atom == window->display->atom_net_wm_icon)
{ {
meta_verbose ("Property notify on %s for NET_WM_ICON\n", window->desc); meta_verbose ("Property notify on %s for NET_WM_ICON\n", window->desc);
update_icon (window); update_icon (window, TRUE);
} }
return TRUE; return TRUE;
@ -2742,6 +2745,11 @@ static int
update_wm_hints (MetaWindow *window) update_wm_hints (MetaWindow *window)
{ {
XWMHints *hints; XWMHints *hints;
Pixmap old_icon;
Pixmap old_mask;
old_icon = window->icon_pixmap;
old_mask = window->icon_mask;
/* Fill in defaults */ /* Fill in defaults */
window->input = FALSE; window->input = FALSE;
@ -2770,6 +2778,14 @@ update_wm_hints (MetaWindow *window)
if (hints->flags & IconMaskHint) if (hints->flags & IconMaskHint)
window->icon_mask = hints->icon_mask; window->icon_mask = hints->icon_mask;
if (window->icon_pixmap != old_icon ||
window->icon_mask != old_mask)
{
meta_verbose ("Icon pixmap or mask changed in WM_HINTS for %s\n",
window->desc);
update_icon (window, FALSE);
}
meta_verbose ("Read WM_HINTS input: %d iconic: %d group leader: 0x%ld\n", meta_verbose ("Read WM_HINTS input: %d iconic: %d group leader: 0x%ld\n",
window->input, window->initially_iconic, window->input, window->initially_iconic,
window->xgroup_leader); window->xgroup_leader);
@ -3025,9 +3041,15 @@ meta_window_get_icon_geometry (MetaWindow *window,
result = meta_error_trap_pop (window->display); result = meta_error_trap_pop (window->display);
if (result != Success || type != XA_CARDINAL || nitems != 4) if (result != Success || type != XA_CARDINAL)
return FALSE; return FALSE;
if (nitems != 4)
{
XFree (geometry);
return FALSE;
}
if (rect) if (rect)
{ {
rect->x = geometry[0]; rect->x = geometry[0];
@ -3442,7 +3464,7 @@ update_icon_name (MetaWindow *window)
text.format == 8 && text.format == 8 &&
g_utf8_validate (text.value, text.nitems, NULL)) g_utf8_validate (text.value, text.nitems, NULL))
{ {
meta_verbose ("Using _NET_WM_ICON_NAME for new title of %s: '%s'\n", meta_verbose ("Using _NET_WM_ICON_NAME for new icon name of %s: '%s'\n",
window->desc, text.value); window->desc, text.value);
window->icon_name = g_strdup (text.value); window->icon_name = g_strdup (text.value);
@ -3498,26 +3520,266 @@ update_icon_name (MetaWindow *window)
return meta_error_trap_pop (window->display); return meta_error_trap_pop (window->display);
} }
static int static gboolean
update_icon (MetaWindow *window) find_best_size (gulong *data,
gulong nitems,
int *width,
int *height,
gulong **start)
{ {
meta_error_trap_push (window->display); int best_w;
int best_h;
gulong *best_start;
#if 0 best_w = 0;
best_h = 0;
best_start = NULL;
while (nitems > 0)
{
int w, h;
gboolean replace;
replace = FALSE;
if (nitems < 3)
{
meta_verbose ("_NET_WM_ICON contained too little data\n");
return FALSE;
}
w = data[0];
h = data[1];
if (nitems < ((w * h) + 2))
{
meta_verbose ("_NET_WM_ICON contained too little data\n");
return FALSE;
}
if (best_start == NULL)
{
replace = TRUE;
}
else
{
/* work with averages */
const int ideal_size = META_ICON_WIDTH * META_ICON_HEIGHT;
int best_size = (best_w + best_h) / 2;
int this_size = (w + h) / 2;
/* larger than desired is always better than smaller */
if (best_size < ideal_size &&
this_size >= ideal_size)
replace = TRUE;
/* if we have too small, pick anything bigger */
else if (best_size < ideal_size &&
this_size > best_size)
replace = TRUE;
/* if we have too large, pick anything smaller
* but still >= the ideal
*/
else if (best_size > ideal_size &&
this_size >= ideal_size &&
this_size < best_size)
replace = TRUE;
}
if (replace)
{
best_start = data + 2;
best_w = w;
best_h = h;
}
nitems -= (w * h) + 2;
}
if (best_start)
{
*start = best_start;
*width = best_w;
*height = best_h;
return TRUE;
}
else
return FALSE;
}
static gboolean
read_rgb_icon (MetaWindow *window,
int *width,
int *height,
guchar **pixdata)
{
Atom type;
int format;
gulong nitems;
gulong bytes_after;
int result;
gulong *data; /* FIXME should be guint? */
gulong *best;
int i;
int w, h;
guchar *p;
if (sizeof (gulong) != 4)
meta_warning ("%s: Whoops, I think this function may be broken on 64-bit\n",
__FUNCTION__);
meta_error_trap_push (window->display);
type = None;
data = NULL;
XGetWindowProperty (window->display->xdisplay,
window->xwindow,
window->display->atom_net_wm_icon,
0, G_MAXLONG,
False, XA_CARDINAL, &type, &format, &nitems,
&bytes_after, ((guchar **)&data));
result = meta_error_trap_pop (window->display);
if (result != Success || type != XA_CARDINAL)
{
meta_verbose ("%s doesn't seem to set _NET_WM_ICON\n",
window->desc);
return FALSE;
}
if (!find_best_size (data, nitems, &w, &h, &best))
{
XFree (data);
return FALSE;
}
*width = w;
*height = h;
*pixdata = g_new (guchar, w * h);
p = *pixdata;
/* One could speed this up a lot. */
i = 0;
while (i < w * h)
{
guint argb;
guint rgba;
argb = best[i];
rgba = (argb << 8) & (argb >> 24);
*p = rgba >> 24;
++p;
*p = (rgba >> 16) & 0xff;
++p;
*p = (rgba >> 8) & 0xff;
++p;
*p = rgba & 0xff;
++p;
++i;
}
XFree (data);
return TRUE;
}
static void
clear_icon (MetaWindow *window)
{
if (window->icon) if (window->icon)
{ {
g_object_unref (G_OBJECT (window->icon)); g_object_unref (G_OBJECT (window->icon));
window->icon = NULL; window->icon = NULL;
} }
#endif }
/* FIXME */ static void
free_pixels (guchar *pixels, gpointer data)
{
g_free (pixels);
}
/* Fallback */ static int
update_icon (MetaWindow *window,
gboolean reload_rgb_icon)
{
if (reload_rgb_icon)
{
guchar *pixdata;
int w, h;
pixdata = NULL;
if (read_rgb_icon (window, &w, &h, &pixdata))
{
GdkPixbuf *unscaled;
meta_verbose ("successfully read RGBA icon fro _NET_WM_ICON\n");
window->using_rgb_icon = TRUE;
clear_icon (window);
unscaled = gdk_pixbuf_new_from_data (pixdata,
GDK_COLORSPACE_RGB,
TRUE,
8,
w, h, w,
free_pixels,
NULL);
if (w != META_ICON_WIDTH || h != META_ICON_HEIGHT)
{
/* FIXME should keep aspect ratio, but for now assuming
* a square source icon
*/
window->icon = gdk_pixbuf_scale_simple (unscaled,
META_ICON_WIDTH,
META_ICON_HEIGHT,
GDK_INTERP_BILINEAR);
g_object_unref (G_OBJECT (unscaled));
}
else
{
window->icon = unscaled;
}
return Success;
}
else
{
if (window->using_rgb_icon)
clear_icon (window);
window->using_rgb_icon = FALSE;
/* We'll try to fall back to something else below. */
}
}
else if (window->using_rgb_icon)
{
/* There's no way we want to use the fallbacks,
* keep using this.
*/
return Success;
}
/* Fallback to pixmap + mask */
/* FIXME well, I'm not sure how to deal with the mask */
/* FIXME for that matter, I don't know how we get the
* icon pixmap as pixbuf without knowing if it's a bitmap,
* so we may be entirely hosed. I guess we can try to get it
* with a nice error trap.
*/
/* Fallback to a default icon */
if (window->icon == NULL) if (window->icon == NULL)
window->icon = meta_ui_get_default_window_icon (window->screen->ui); window->icon = meta_ui_get_default_window_icon (window->screen->ui);
return meta_error_trap_pop (window->display); return Success;
} }
static void static void

View File

@ -70,11 +70,14 @@ struct _MetaWindow
Window xgroup_leader; Window xgroup_leader;
Window xclient_leader; Window xclient_leader;
/* Initial workspace property */
int initial_workspace;
Pixmap icon_pixmap; Pixmap icon_pixmap;
Pixmap icon_mask; Pixmap icon_mask;
/* Initial workspace property */ /* Whether ->icon is from NET_WM_ICON instead of pixmap */
int initial_workspace; guint using_rgb_icon : 1;
/* Whether we're maximized */ /* Whether we're maximized */
guint maximized : 1; guint maximized : 1;

View File

@ -28,11 +28,12 @@
#include <unistd.h> #include <unistd.h>
static void set_up_the_evil (void); static void set_up_the_evil (void);
static void set_up_icon_windows (void);
static void static void
usage (void) usage (void)
{ {
g_print ("wm-tester [--evil]\n"); g_print ("wm-tester [--evil] [--icon-windows]\n");
exit (0); exit (0);
} }
@ -41,10 +42,12 @@ main (int argc, char **argv)
{ {
int i; int i;
gboolean do_evil; gboolean do_evil;
gboolean do_icon_windows;
gtk_init (&argc, &argv); gtk_init (&argc, &argv);
do_evil = FALSE; do_evil = FALSE;
do_icon_windows = FALSE;
i = 1; i = 1;
while (i < argc) while (i < argc)
@ -57,6 +60,8 @@ main (int argc, char **argv)
usage (); usage ();
else if (strcmp (arg, "--evil") == 0) else if (strcmp (arg, "--evil") == 0)
do_evil = TRUE; do_evil = TRUE;
else if (strcmp (arg, "--icon-windows") == 0)
do_icon_windows = TRUE;
else else
usage (); usage ();
@ -64,12 +69,15 @@ main (int argc, char **argv)
} }
/* Be sure some option was provided */ /* Be sure some option was provided */
if (! (do_evil)) if (! (do_evil || do_icon_windows))
return 1; return 1;
if (do_evil) if (do_evil)
set_up_the_evil (); set_up_the_evil ();
if (do_icon_windows)
set_up_icon_windows ();
gtk_main (); gtk_main ();
return 0; return 0;
@ -156,3 +164,64 @@ set_up_the_evil (void)
g_timeout_add (40, evil_timeout, NULL); g_timeout_add (40, evil_timeout, NULL);
} }
static void
set_up_icon_windows (void)
{
int i;
int n_windows;
/* Create some windows */
n_windows = 9;
i = 0;
while (i < n_windows)
{
GtkWidget *w;
GtkWidget *c;
GList *icons;
GdkPixbuf *pix;
w = gtk_window_new (GTK_WINDOW_TOPLEVEL);
c = gtk_button_new_with_label ("Icon window");
gtk_container_add (GTK_CONTAINER (w), c);
gtk_widget_realize (w);
icons = NULL;
pix = gtk_widget_render_icon (w,
GTK_STOCK_SAVE,
GTK_ICON_SIZE_LARGE_TOOLBAR,
NULL);
icons = g_list_append (icons, pix);
if (i % 2)
{
pix = gtk_widget_render_icon (w,
GTK_STOCK_SAVE,
GTK_ICON_SIZE_DIALOG,
NULL);
icons = g_list_append (icons, pix);
}
if (i % 3)
{
pix = gtk_widget_render_icon (w,
GTK_STOCK_SAVE,
GTK_ICON_SIZE_MENU,
NULL);
icons = g_list_append (icons, pix);
}
if (!gdk_window_set_icon_list (w->window, icons))
g_warning ("_NET_WM_ICON not supported?");
g_list_foreach (icons, (GFunc) g_object_unref, NULL);
g_list_free (icons);
gtk_widget_show_all (w);
++i;
}
}