From 04e09d4c564dc5efea47b60c24309617c9d961c8 Mon Sep 17 00:00:00 2001 From: Havoc Pennington Date: Mon, 20 Aug 2001 01:42:44 +0000 Subject: [PATCH] we support _NET_WM_ICON 2001-08-19 Havoc Pennington * 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 --- ChangeLog | 16 +++ src/display.c | 4 +- src/display.h | 1 + src/run-metacity.sh | 4 + src/screen.c | 28 +++- src/screen.h | 5 + src/tabpopup.c | 2 - src/ui.c | 35 +++++ src/ui.h | 9 ++ src/window.c | 298 ++++++++++++++++++++++++++++++++++++++++--- src/window.h | 11 +- src/wm-tester/main.c | 77 ++++++++++- 12 files changed, 460 insertions(+), 30 deletions(-) diff --git a/ChangeLog b/ChangeLog index b595e09d4..9852cb0fb 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,19 @@ +2001-08-19 Havoc Pennington + + * 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 * src/display.c (meta_display_grab_window_buttons): remove XSync, diff --git a/src/display.c b/src/display.c index bc4b819a5..6d7f6e519 100644 --- a/src/display.c +++ b/src/display.c @@ -132,7 +132,8 @@ meta_display_open (const char *name) "_NET_WM_ICON_NAME", "_NET_WM_ICON", "_NET_WM_ICON_GEOMETRY", - "UTF8_STRING" + "UTF8_STRING", + "WM_ICON_SIZE" }; 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_geometry = atoms[38]; display->atom_utf8_string = atoms[39]; + display->atom_wm_icon_size = atoms[40]; /* Offscreen unmapped window used for _NET_SUPPORTING_WM_CHECK, * created in screen_new diff --git a/src/display.h b/src/display.h index 1aa471623..b0a5478a4 100644 --- a/src/display.h +++ b/src/display.h @@ -97,6 +97,7 @@ struct _MetaDisplay Atom atom_net_wm_icon; Atom atom_net_wm_icon_geometry; Atom atom_utf8_string; + Atom atom_wm_icon_size; /* This is the actual window from focus events, * not the one we last set diff --git a/src/run-metacity.sh b/src/run-metacity.sh index 01dd44328..8daa023da 100755 --- a/src/run-metacity.sh +++ b/src/run-metacity.sh @@ -22,6 +22,10 @@ if test -n "$EVIL_TEST"; then TEST_CLIENT='./wm-tester/wm-tester --evil' fi +if test -n "$ICON_TEST"; then + TEST_CLIENT='./wm-tester/wm-tester --icon-windows' +fi + if test -z "$ONLY_WM"; then Xnest -ac :1 -scrns $SCREENS -geometry 640x480 -bw 15 & ## usleep 800000 diff --git a/src/screen.c b/src/screen.c index ffad0e067..b1795f0c4 100644 --- a/src/screen.c +++ b/src/screen.c @@ -72,7 +72,7 @@ set_wm_check_hint (MetaScreen *screen) static int set_supported_hint (MetaScreen *screen) { -#define N_SUPPORTED 21 +#define N_SUPPORTED 22 #define N_WIN_SUPPORTED 1 Atom atoms[N_SUPPORTED]; @@ -97,6 +97,7 @@ set_supported_hint (MetaScreen *screen) atoms[18] = screen->display->atom_net_client_list_stacking; atoms[19] = screen->display->atom_net_wm_state_skip_taskbar; atoms[20] = screen->display->atom_net_wm_state_skip_pager; + atoms[21] = screen->display->atom_net_wm_icon; XChangeProperty (screen->display->xdisplay, screen->xroot, screen->display->atom_net_wm_supported, @@ -115,6 +116,29 @@ set_supported_hint (MetaScreen *screen) #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* meta_screen_new (MetaDisplay *display, int number) @@ -177,6 +201,8 @@ meta_screen_new (MetaDisplay *display, display->leader_window = XCreateSimpleWindow (display->xdisplay, screen->xroot, -100, -100, 1, 1, 0, 0, 0); + + set_wm_icon_size_hint (screen); set_supported_hint (screen); diff --git a/src/screen.h b/src/screen.h index f04e5bc42..e31268491 100644 --- a/src/screen.h +++ b/src/screen.h @@ -26,6 +26,11 @@ #include #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, gpointer user_data); diff --git a/src/tabpopup.c b/src/tabpopup.c index 0a1b055e3..ca94bbf83 100644 --- a/src/tabpopup.c +++ b/src/tabpopup.c @@ -287,6 +287,4 @@ meta_ui_tab_popup_select (MetaTabPopup *popup, tmp = tmp->next; } - - meta_bug ("Selected nonexistent entry 0x%lx in tab popup\n", xwindow); } diff --git a/src/ui.c b/src/ui.c index fe3de2c2e..f11b198c7 100644 --- a/src/ui.c +++ b/src/ui.c @@ -333,6 +333,41 @@ meta_gdk_pixbuf_get_from_window (GdkPixbuf *dest, 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 meta_ui_push_delay_exposes (MetaUI *ui) { diff --git a/src/ui.h b/src/ui.h index c9ce0b04d..9fc354c9c 100644 --- a/src/ui.h +++ b/src/ui.h @@ -110,6 +110,15 @@ GdkPixbuf* meta_gdk_pixbuf_get_from_window (GdkPixbuf *dest, int width, 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, * then we need to handle exposes after doing that, instead of * during it diff --git a/src/window.c b/src/window.c index 9e45d46fc..591582b4d 100644 --- a/src/window.c +++ b/src/window.c @@ -59,7 +59,8 @@ static int update_role (MetaWindow *window); static int update_net_wm_type (MetaWindow *window); static int update_initial_workspace (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_features (MetaWindow *window); static int set_wm_state (MetaWindow *window, @@ -317,6 +318,8 @@ meta_window_new (MetaDisplay *display, Window xwindow, window->icon_pixmap = None; window->icon_mask = None; + + window->using_rgb_icon = FALSE; window->type = META_WINDOW_NORMAL; window->type_atom = None; @@ -328,7 +331,7 @@ meta_window_new (MetaDisplay *display, Window xwindow, update_size_hints (window); update_title (window); - update_protocols (window); + update_protocols (window); update_wm_hints (window); update_net_wm_state (window); update_mwm_hints (window); @@ -339,7 +342,8 @@ meta_window_new (MetaDisplay *display, Window xwindow, update_net_wm_type (window); update_initial_workspace (window); update_icon_name (window); - update_icon (window); + /* should come after wm_hints */ + update_icon (window, TRUE); if (!window->mapped && (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); update_wm_hints (window); - update_icon (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) { meta_verbose ("Property notify on %s for NET_WM_ICON\n", window->desc); - update_icon (window); + update_icon (window, TRUE); } return TRUE; @@ -2742,7 +2745,12 @@ static int update_wm_hints (MetaWindow *window) { XWMHints *hints; + Pixmap old_icon; + Pixmap old_mask; + old_icon = window->icon_pixmap; + old_mask = window->icon_mask; + /* Fill in defaults */ window->input = FALSE; window->initially_iconic = FALSE; @@ -2769,6 +2777,14 @@ update_wm_hints (MetaWindow *window) if (hints->flags & IconMaskHint) 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", window->input, window->initially_iconic, @@ -3025,8 +3041,14 @@ meta_window_get_icon_geometry (MetaWindow *window, result = meta_error_trap_pop (window->display); - if (result != Success || type != XA_CARDINAL || nitems != 4) - return FALSE; + if (result != Success || type != XA_CARDINAL) + return FALSE; + + if (nitems != 4) + { + XFree (geometry); + return FALSE; + } if (rect) { @@ -3442,7 +3464,7 @@ update_icon_name (MetaWindow *window) text.format == 8 && 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->icon_name = g_strdup (text.value); @@ -3498,26 +3520,266 @@ update_icon_name (MetaWindow *window) return meta_error_trap_pop (window->display); } -static int -update_icon (MetaWindow *window) -{ - meta_error_trap_push (window->display); +static gboolean +find_best_size (gulong *data, + gulong nitems, + int *width, + int *height, + gulong **start) +{ + 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) { g_object_unref (G_OBJECT (window->icon)); window->icon = NULL; } -#endif - - /* FIXME */ +} - /* Fallback */ +static void +free_pixels (guchar *pixels, gpointer data) +{ + g_free (pixels); +} + +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) window->icon = meta_ui_get_default_window_icon (window->screen->ui); - return meta_error_trap_pop (window->display); + return Success; } static void diff --git a/src/window.h b/src/window.h index 3757d8d4c..a70649dbb 100644 --- a/src/window.h +++ b/src/window.h @@ -70,11 +70,14 @@ struct _MetaWindow Window xgroup_leader; Window xclient_leader; - Pixmap icon_pixmap; - Pixmap icon_mask; - /* Initial workspace property */ - int initial_workspace; + int initial_workspace; + + Pixmap icon_pixmap; + Pixmap icon_mask; + + /* Whether ->icon is from NET_WM_ICON instead of pixmap */ + guint using_rgb_icon : 1; /* Whether we're maximized */ guint maximized : 1; diff --git a/src/wm-tester/main.c b/src/wm-tester/main.c index 9a2da68fe..f58742d64 100644 --- a/src/wm-tester/main.c +++ b/src/wm-tester/main.c @@ -28,11 +28,12 @@ #include static void set_up_the_evil (void); +static void set_up_icon_windows (void); static void usage (void) { - g_print ("wm-tester [--evil]\n"); + g_print ("wm-tester [--evil] [--icon-windows]\n"); exit (0); } @@ -41,10 +42,12 @@ main (int argc, char **argv) { int i; gboolean do_evil; - + gboolean do_icon_windows; + gtk_init (&argc, &argv); do_evil = FALSE; + do_icon_windows = FALSE; i = 1; while (i < argc) @@ -57,6 +60,8 @@ main (int argc, char **argv) usage (); else if (strcmp (arg, "--evil") == 0) do_evil = TRUE; + else if (strcmp (arg, "--icon-windows") == 0) + do_icon_windows = TRUE; else usage (); @@ -64,12 +69,15 @@ main (int argc, char **argv) } /* Be sure some option was provided */ - if (! (do_evil)) + if (! (do_evil || do_icon_windows)) return 1; if (do_evil) set_up_the_evil (); - + + if (do_icon_windows) + set_up_icon_windows (); + gtk_main (); return 0; @@ -156,3 +164,64 @@ set_up_the_evil (void) 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; + } +}