From 39df21227d00fa4c2d37b413a5432c6a36b61d98 Mon Sep 17 00:00:00 2001 From: Havoc Pennington Date: Sun, 16 Sep 2001 00:30:45 +0000 Subject: [PATCH] add support for a mini icon in the titlebar (update_icon): re-enable 2001-09-15 Havoc Pennington * src/window.c: add support for a mini icon in the titlebar (update_icon): re-enable support for _NET_WM_ICON * src/session.c (save_state): add an ferror check when writing session file --- ChangeLog | 13 ++ configure.in | 10 +- src/Makefile.am | 8 +- src/core.c | 16 ++ src/core.h | 3 + src/frames.c | 71 +++++++- src/main.c | 2 +- src/msm/client.c | 30 +--- src/msm/gtkdisclosurebox.c | 359 +++++++++++++++++++++++++++++++++++++ src/msm/gtkdisclosurebox.h | 76 ++++++++ src/msm/props.c | 141 +++++++++++++++ src/msm/props.h | 28 +++ src/msm/session.c | 272 +++++++++++++++++++++++++++- src/screen.h | 2 + src/session.c | 24 ++- src/ui.c | 10 ++ src/ui.h | 1 + src/window.c | 142 +++++++++++---- src/window.h | 1 + 19 files changed, 1127 insertions(+), 82 deletions(-) create mode 100644 src/msm/gtkdisclosurebox.c create mode 100644 src/msm/gtkdisclosurebox.h diff --git a/ChangeLog b/ChangeLog index 7ff916ddb..c567d9769 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,16 @@ +2001-09-15 Havoc Pennington + + * src/window.c: add support for a mini icon in the titlebar + (update_icon): re-enable support for _NET_WM_ICON + + * src/session.c (save_state): add an ferror check when writing + session file + +2001-09-11 Havoc Pennington + + * src/main.c (usage): exit with error code on usage() (kind of + wrong for --help, but oh well). + 2001-09-11 Havoc Pennington * src/window.c: fix up handling of text properties, so we diff --git a/configure.in b/configure.in index 4ce71e878..2cbb62ca4 100644 --- a/configure.in +++ b/configure.in @@ -62,8 +62,13 @@ if test "$found_sm" = "true"; then AC_DEFINE(HAVE_SM) fi -# Check for shaped window extension -AC_CHECK_LIB(Xext, XShapeCombineMask, AC_DEFINE(HAVE_SHAPE_EXT),,$METACITY_LIBS) +AM_CONDITIONAL(HAVE_SM, test "$found_sm" = "true") + +MSM_CFLAGS=$METACITY_CFLAGS +MSM_LIBS=$METACITY_LIBS + +AC_SUBST(MSM_CFLAGS) +AC_SUBST(MSM_LIBS) HOST_ALIAS=$host_alias AC_SUBST(HOST_ALIAS) @@ -72,6 +77,7 @@ AC_OUTPUT([ Makefile src/Makefile src/wm-tester/Makefile +src/msm/Makefile ]) diff --git a/src/Makefile.am b/src/Makefile.am index 25d930735..2019534c3 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -1,5 +1,11 @@ -SUBDIRS=wm-tester +if HAVE_SM +SM_SUBDIRS=msm +else +SM_SUBDIRS= +endif + +SUBDIRS=wm-tester $(SM_SUBDIRS) INCLUDES=@METACITY_CFLAGS@ -DMETACITY_LIBEXECDIR=\"$(libexecdir)\" -DHOST_ALIAS=\"@HOST_ALIAS@\" diff --git a/src/core.c b/src/core.c index ae36b4bcf..399fe6d29 100644 --- a/src/core.c +++ b/src/core.c @@ -60,6 +60,22 @@ meta_core_get_frame_flags (Display *xdisplay, return meta_frame_get_flags (window->frame); } +GdkPixbuf* +meta_core_get_mini_icon (Display *xdisplay, + Window frame_xwindow) +{ + MetaDisplay *display; + MetaWindow *window; + + display = meta_display_for_x_display (xdisplay); + window = meta_display_lookup_x_window (display, frame_xwindow); + + if (window == NULL || window->frame == NULL) + meta_bug ("No such frame window 0x%lx!\n", frame_xwindow); + + return window->mini_icon; +} + void meta_core_queue_frame_resize (Display *xdisplay, Window frame_xwindow) diff --git a/src/core.h b/src/core.h index 24b2c6339..bf8ccd119 100644 --- a/src/core.h +++ b/src/core.h @@ -36,6 +36,9 @@ void meta_core_get_frame_size (Display *xdisplay, MetaFrameFlags meta_core_get_frame_flags (Display *xdisplay, Window frame_xwindow); +GdkPixbuf* meta_core_get_mini_icon (Display *xdisplay, + Window frame_xwindow); + void meta_core_queue_frame_resize (Display *xdisplay, Window frame_xwindow); diff --git a/src/frames.c b/src/frames.c index cf006f5b4..22d5fd09f 100644 --- a/src/frames.c +++ b/src/frames.c @@ -1740,11 +1740,78 @@ meta_frames_expose_event (GtkWidget *widget, if (frame->layout) { + PangoRectangle layout_rect; + int x, y, icon_x, icon_y; + GdkPixbuf *icon; + int icon_w, icon_h; + int area_w, area_h; + +#define ICON_TEXT_SPACING 2 + + icon = meta_core_get_mini_icon (gdk_display, + frame->xwindow); + + icon_w = gdk_pixbuf_get_width (icon); + icon_h = gdk_pixbuf_get_height (icon); + + pango_layout_get_pixel_extents (frame->layout, + NULL, + &layout_rect); + + /* corner of whole title area */ + x = fgeom.title_rect.x + frames->props->text_border.left; + y = fgeom.title_rect.y + frames->props->text_border.top; + + area_w = fgeom.title_rect.width - + frames->props->text_border.left - + frames->props->text_border.right; + + area_h = fgeom.title_rect.height - + frames->props->text_border.top - + frames->props->text_border.bottom; + + /* center icon vertically */ + icon_y = y + MAX ((area_h - icon_h) / 2, 0); + /* center text vertically */ + y = y + MAX ((area_h - layout_rect.height) / 2, 0); + + /* Center icon + text combo */ + icon_x = x + MAX ((area_w - layout_rect.width - icon_w - ICON_TEXT_SPACING) / 2, 0); + x = icon_x + icon_w + ICON_TEXT_SPACING; + gdk_gc_set_clip_rectangle (layout_gc, &clip); + + { + /* grumble, render_to_drawable_alpha does not accept a clip + * mask, so we have to go through some BS + */ + GdkRectangle pixbuf_rect; + GdkRectangle draw_rect; + + pixbuf_rect.x = icon_x; + pixbuf_rect.y = icon_y; + pixbuf_rect.width = icon_w; + pixbuf_rect.height = icon_h; + + if (gdk_rectangle_intersect (&clip, &pixbuf_rect, &draw_rect)) + { + gdk_pixbuf_render_to_drawable_alpha (icon, + frame->window, + draw_rect.x - pixbuf_rect.x, + draw_rect.y - pixbuf_rect.y, + draw_rect.x, draw_rect.y, + draw_rect.width, + draw_rect.height, + GDK_PIXBUF_ALPHA_FULL, + 128, + GDK_RGB_DITHER_NORMAL, + 0, 0); + } + } + gdk_draw_layout (frame->window, layout_gc, - fgeom.title_rect.x + frames->props->text_border.left, - fgeom.title_rect.y + frames->props->text_border.top, + x, y, frame->layout); gdk_gc_set_clip_rectangle (layout_gc, NULL); } diff --git a/src/main.c b/src/main.c index 52a393f15..8f3bb411e 100644 --- a/src/main.c +++ b/src/main.c @@ -42,7 +42,7 @@ static void usage (void) { g_print ("metacity [--disable-sm] [--sm-client-id=ID] [--display=DISPLAY]\n"); - exit (0); + exit (1); } int diff --git a/src/msm/client.c b/src/msm/client.c index f6a17ed64..3cebf3592 100644 --- a/src/msm/client.c +++ b/src/msm/client.c @@ -288,25 +288,9 @@ msm_client_set_property_taking_ownership (MsmClient *client, SmProp *prop) { /* we own prop which should be freed with SmFreeProperty() */ - GList *list; - if (prop->name == NULL) - { - SmFreeProperty (prop); - return; - } - - list = proplist_find_link_by_name (client->properties, prop->name); - if (list) - { - SmFreeProperty (list->data); - list->data = prop; - } - else - { - client->properties = g_list_prepend (client->properties, - prop); - } + /* pass our ownership into the proplist */ + client->properties = proplist_replace (client->properties, prop); /* update pieces of the client struct */ if (strcmp (prop->name, "SmRestartStyleHint") == 0) @@ -323,15 +307,7 @@ void msm_client_unset_property (MsmClient *client, const char *name) { - GList *list; - - list = proplist_find_link_by_name (client->properties, name); - if (list) - { - SmFreeProperty (list->data); - client->properties = g_list_delete_link (client->properties, - list); - } + client->properties = proplist_delete (client->properties, name); /* Return to default values */ if (strcmp (name, "SmRestartStyleHint") == 0) diff --git a/src/msm/gtkdisclosurebox.c b/src/msm/gtkdisclosurebox.c new file mode 100644 index 000000000..b26e86947 --- /dev/null +++ b/src/msm/gtkdisclosurebox.c @@ -0,0 +1,359 @@ +/* GTK - The GIMP Toolkit + * Copyright (C) 1995-1997 Peter Mattis, Spencer Kimball and Josh MacDonald + * this file Copyright (C) 2001 Havoc Pennington + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +/* + * Modified by the GTK+ Team and others 1997-2000. See the AUTHORS + * file for a list of people on the GTK+ Team. See the ChangeLog + * files for a list of changes. These files are distributed with + * GTK+ at ftp://ftp.gtk.org/pub/gtk/. + */ + + + +/* FIXME implementation contains a bunch of cut-and-paste from GtkFrame + * that would be easy to avoid by adding an "int space_before_label_widget" + * in GtkFrame that was overridden by subclasses. + */ + +/* FIXME the whole GtkFrame derivation idea is fucked since we can't get + * the click event on the arrow. + */ + + +#define ARROW_SIZE 12 +#define ARROW_PAD 2 + +enum { + PROP_0, + PROP_DISCLOSED, + PROP_LAST +}; + +static void gtk_disclosure_box_class_init (GtkDisclosureBoxClass *klass); +static void gtk_disclosure_box_init (GtkDisclosureBox *box); +static void gtk_disclosure_box_set_property (GObject *object, + guint prop_id, + const GValue *value, + GParamSpec *pspec); +static void gtk_disclosure_box_get_property (GObject *object, + guint prop_id, + GValue *value, + GParamSpec *pspec); + +static void gtk_disclosure_box_paint (GtkWidget *widget, + GdkRectangle *area); +static gint gtk_disclosure_box_expose (GtkWidget *widget, + GdkEventExpose *event); +static void gtk_disclosure_box_size_request (GtkWidget *widget, + GtkRequisition *requisition); +static void gtk_disclosure_box_size_allocate (GtkWidget *widget, + GtkAllocation *allocation); + +static void gtk_frame_compute_child_allocation (GtkFrame *frame, + GtkAllocation *child_allocation); + +GType +gtk_disclosure_box_get_type (void) +{ + static GType disclosure_box_type = 0; + + if (!disclosure_box_type) + { + static const GtkTypeInfo disclosure_box_info = + { + "GtkDisclosureBox", + sizeof (GtkDisclosureBox), + sizeof (GtkDisclosureBoxClass), + (GtkClassInitFunc) gtk_disclosure_box_class_init, + (GtkObjectInitFunc) gtk_disclosure_box_init, + /* reserved_1 */ NULL, + /* reserved_2 */ NULL, + (GtkClassInitFunc) NULL, + }; + + disclosure_box_type = gtk_type_unique (gtk_box_get_type (), &disclosure_box_info); + } + + return disclosure_box_type; +} + +static void +gtk_disclosure_box_class_init (GtkDisclosureBoxClass *class) +{ + GtkWidgetClass *widget_class; + GObjectClass *gobject_class; + GtkContainerClass *container_class; + GtkFrameClass *frame_class; + + gobject_class = G_OBJECT_CLASS (class); + widget_class = GTK_WIDGET_CLASS (class); + container_class = GTK_CONTAINER_CLASS (class); + frame_class = GTK_FRAME_CLASS (class); + + gobject_class->set_property = gtk_disclosure_box_set_property; + gobject_class->get_property = gtk_disclosure_box_get_property; + + widget_class->size_request = gtk_disclosure_box_size_request; + widget_class->size_allocate = gtk_disclosure_box_size_allocate; + widget_class->expose_event = gtk_disclosure_box_expose; +} + +static void +gtk_disclosure_box_init (GtkDisclosureBox *disclosure_box) +{ + +} + +GtkWidget* +gtk_disclosure_box_new (const char *label) +{ + return g_object_new (GTK_TYPE_DISCLOSURE_BOX, "label", label, NULL); +} + +static void +gtk_disclosure_box_set_property (GObject *object, + guint prop_id, + const GValue *value, + GParamSpec *pspec) +{ + GtkDisclosureBox *box; + + box = GTK_DISCLOSURE_BOX (object); + + switch (prop_id) + { + case PROP_DISCLOSED: + gtk_disclosure_box_set_disclosed (box, + g_value_get_boolean (value)); + break; + + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static void +gtk_disclosure_box_get_property (GObject *object, + guint prop_id, + GValue *value, + GParamSpec *pspec) +{ + GtkDisclosureBox *box; + + box = GTK_DISCLOSURE_BOX (object); + + switch (prop_id) + { + case PROP_DISCLOSED: + g_value_set_boolean (value, box->disclosed); + break; + + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +void +gtk_disclosure_box_set_disclosed (GtkDisclosureBox *box, + gboolean disclosed) +{ + g_return_if_fail (GTK_IS_DISCLOSURE_BOX (box)); + + disclosed = disclosed != FALSE; + + if (disclosed != box->disclosed) + { + box->disclosed = disclosed; + gtk_widget_queue_resize (GTK_WIDGET (box)); + } +} + +gboolean +gtk_disclosure_box_get_disclosed (GtkDisclosureBox *box) +{ + g_return_val_if_fail (GTK_IS_DISCLOSURE_BOX (box), FALSE); + + return box->disclosed; +} + + +static void +gtk_disclosure_box_paint (GtkWidget *widget, + GdkRectangle *area) +{ + GtkFrame *frame; + gint x, y, width, height; + + if (GTK_WIDGET_DRAWABLE (widget)) + { + frame = GTK_FRAME (widget); + + x = frame->child_allocation.x - widget->style->xthickness; + y = frame->child_allocation.y - widget->style->ythickness; + width = frame->child_allocation.width + 2 * widget->style->xthickness; + height = frame->child_allocation.height + 2 * widget->style->ythickness; + + if (frame->label_widget) + { + GtkRequisition child_requisition; + gfloat xalign; + gint height_extra; + gint x2; + + gtk_widget_get_child_requisition (frame->label_widget, &child_requisition); + + if (gtk_widget_get_direction (widget) == GTK_TEXT_DIR_LTR) + xalign = frame->label_xalign; + else + xalign = 1 - frame->label_xalign; + + height_extra = MAX (0, child_requisition.height - widget->style->xthickness); + y -= height_extra * (1 - frame->label_yalign); + height += height_extra * (1 - frame->label_yalign); + + x2 = widget->style->xthickness + (frame->child_allocation.width - child_requisition.width - 2 * LABEL_PAD - 2 * LABEL_SIDE_PAD) * xalign + LABEL_SIDE_PAD; + + + gtk_paint_shadow_gap (widget->style, widget->window, + GTK_STATE_NORMAL, frame->shadow_type, + area, widget, "frame", + x, y, width, height, + GTK_POS_TOP, + x2 + ARROW_SIZE + ARROW_PAD * 2, child_requisition.width + 2 * LABEL_PAD); + + gtk_paint_arrow (widget->style, widget->window, + widget->state, GTK_SHADOW_OUT, + area, widget, "arrow", + GTK_DISCLOSURE_BOX (widget)->disclosed ? + GTK_ARROW_RIGHT : GTK_ARROW_DOWN, + TRUE, + x2 + ARROW_PAD, y, ARROW_SIZE, ARROW_SIZE); + } + else + gtk_paint_shadow (widget->style, widget->window, + GTK_STATE_NORMAL, frame->shadow_type, + area, widget, "frame", + x, y, width, height); + } +} + +static gboolean +gtk_disclosure_box_expose (GtkWidget *widget, + GdkEventExpose *event) +{ + if (GTK_WIDGET_DRAWABLE (widget)) + { + gtk_disclosure_box_paint (widget, &event->area); + + (* GTK_WIDGET_CLASS (parent_class)->expose_event) (widget, event); + } + + return FALSE; +} + +static void +gtk_disclosure_box_size_request (GtkWidget *widget, + GtkRequisition *requisition) +{ + GtkFrame *frame = GTK_FRAME (widget); + GtkBin *bin = GTK_BIN (widget); + GtkRequisition child_requisition; + + if (frame->label_widget && GTK_WIDGET_VISIBLE (frame->label_widget)) + { + gtk_widget_size_request (frame->label_widget, &child_requisition); + + requisition->width = child_requisition.width + 2 * LABEL_PAD + 2 * LABEL_SIDE_PAD + ARROW_SIZE + ARROW_PAD * 2; + requisition->height = + MAX (0, child_requisition.height - GTK_WIDGET (widget)->style->xthickness); + } + else + { + requisition->width = 0; + requisition->height = 0; + } + + if (bin->child && GTK_WIDGET_VISIBLE (bin->child)) + { + gtk_widget_size_request (bin->child, &child_requisition); + + requisition->width = MAX (requisition->width, child_requisition.width); + requisition->height += child_requisition.height; + } + + requisition->width += (GTK_CONTAINER (widget)->border_width + + GTK_WIDGET (widget)->style->xthickness) * 2; + requisition->height += (GTK_CONTAINER (widget)->border_width + + GTK_WIDGET (widget)->style->ythickness) * 2; +} + +static void +gtk_disclosure_box_size_allocate (GtkWidget *widget, + GtkAllocation *allocation) +{ + GtkFrame *frame = GTK_FRAME (widget); + GtkBin *bin = GTK_BIN (widget); + GtkAllocation new_allocation; + + widget->allocation = *allocation; + + gtk_frame_compute_child_allocation (frame, &new_allocation); + + /* If the child allocation changed, that means that the frame is drawn + * in a new place, so we must redraw the entire widget. + */ + if (GTK_WIDGET_MAPPED (widget) && + (new_allocation.x != frame->child_allocation.x || + new_allocation.y != frame->child_allocation.y || + new_allocation.width != frame->child_allocation.width || + new_allocation.height != frame->child_allocation.height)) + gtk_widget_queue_clear (widget); + + if (bin->child && GTK_WIDGET_VISIBLE (bin->child)) + gtk_widget_size_allocate (bin->child, &new_allocation); + + frame->child_allocation = new_allocation; + + if (frame->label_widget && GTK_WIDGET_VISIBLE (frame->label_widget)) + { + GtkRequisition child_requisition; + GtkAllocation child_allocation; + gfloat xalign; + + gtk_widget_get_child_requisition (frame->label_widget, &child_requisition); + + if (gtk_widget_get_direction (widget) == GTK_TEXT_DIR_LTR) + xalign = frame->label_xalign; + else + xalign = 1 - frame->label_xalign; + + child_allocation.x = frame->child_allocation.x + LABEL_SIDE_PAD + + (frame->child_allocation.width - child_requisition.width - 2 * LABEL_PAD - 2 * LABEL_SIDE_PAD) * xalign + LABEL_PAD + ARROW_SIZE + ARROW_PAD * 2; + child_allocation.width = child_requisition.width; + + child_allocation.y = frame->child_allocation.y - child_requisition.height; + child_allocation.height = child_requisition.height; + + gtk_widget_size_allocate (frame->label_widget, &child_allocation); + } +} + diff --git a/src/msm/gtkdisclosurebox.h b/src/msm/gtkdisclosurebox.h new file mode 100644 index 000000000..5c5b58039 --- /dev/null +++ b/src/msm/gtkdisclosurebox.h @@ -0,0 +1,76 @@ +/* GTK - The GIMP Toolkit + * Copyright (C) 1995-1997 Peter Mattis, Spencer Kimball and Josh MacDonald + * this file Copyright (C) 2001 Havoc Pennington + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +/* + * Modified by the GTK+ Team and others 1997-2000. See the AUTHORS + * file for a list of people on the GTK+ Team. See the ChangeLog + * files for a list of changes. These files are distributed with + * GTK+ at ftp://ftp.gtk.org/pub/gtk/. + */ + +#ifndef __GTK_DISCLOSURE_BOX_H__ +#define __GTK_DISCLOSURE_BOX_H__ + + +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + + +#define GTK_TYPE_DISCLOSURE_BOX (gtk_disclosure_box_get_type ()) +#define GTK_DISCLOSURE_BOX(obj) (GTK_CHECK_CAST ((obj), GTK_TYPE_DISCLOSURE_BOX, GtkDisclosureBox)) +#define GTK_DISCLOSURE_BOX_CLASS(klass) (GTK_CHECK_CLASS_CAST ((klass), GTK_TYPE_DISCLOSURE_BOX, GtkDisclosureBoxClass)) +#define GTK_IS_DISCLOSURE_BOX(obj) (GTK_CHECK_TYPE ((obj), GTK_TYPE_DISCLOSURE_BOX)) +#define GTK_IS_DISCLOSURE_BOX_CLASS(klass) (GTK_CHECK_CLASS_TYPE ((klass), GTK_TYPE_DISCLOSURE_BOX)) +#define GTK_DISCLOSURE_BOX_GET_CLASS(obj) (GTK_CHECK_GET_CLASS ((obj), GTK_TYPE_DISCLOSURE_BOX, GtkDisclosureBoxClass)) + +typedef struct _GtkDisclosureBox GtkDisclosureBox; +typedef struct _GtkDisclosureBoxClass GtkDisclosureBoxClass; + +struct _GtkDisclosureBox +{ + GtkFrame parent_instance; + + guint disclosed : 1; +}; + +struct _GtkDisclosureBoxClass +{ + GtkFrameClass parent_class; +}; + +GType gtk_disclosure_box_get_type (void) G_GNUC_CONST; + +GtkWidget* gtk_disclosure_box_new (const char *label); + +void gtk_disclosure_box_set_disclosed (GtkDisclosureBox *box, + gboolean disclosed); +gboolean gtk_disclosure_box_get_disclosed (GtkDisclosureBox *box); + + +#ifdef __cplusplus +} +#endif /* __cplusplus */ + + +#endif /* __GTK_FRAME_H__ */ diff --git a/src/msm/props.c b/src/msm/props.c index fd0cf8600..e88e3e460 100644 --- a/src/msm/props.c +++ b/src/msm/props.c @@ -82,6 +82,80 @@ proplist_find_string (GList *list, const char *name, return smprop_get_string (prop, result); } +GList* +proplist_replace (GList *list, + SmProp *new_prop) +{ + GList *link; + + link = proplist_find_link_by_name (list, new_prop->name); + if (link) + { + SmFreeProperty (link->data); + link->data = new_prop; + } + else + { + list = g_list_prepend (list, new_prop); + } + + return list; +} + +GList* +proplist_delete (GList *list, + const char *name) +{ + GList *link; + + link = proplist_find_link_by_name (list, name); + if (link) + { + SmFreeProperty (link->data); + list = g_list_delete_link (list, link); + } + + return list; +} + +GList* +proplist_replace_card8 (GList *list, + const char *name, + int value) +{ + SmProp *prop; + + prop = smprop_new_card8 (name, value); + + return proplist_replace (list, prop); +} + +GList* +proplist_replace_string (GList *list, + const char *name, + const char *str, + int len) +{ + SmProp *prop; + + prop = smprop_new_string (name, str, len); + + return proplist_replace (list, prop); +} + +GList* +proplist_replace_vector (GList *list, + const char *name, + int argc, + char **argv) +{ + SmProp *prop; + + prop = smprop_new_vector (name, argc, argv); + + return proplist_replace (list, prop); +} + gboolean proplist_find_vector (GList *list, const char *name, int *argcp, char ***argvp) @@ -206,5 +280,72 @@ smprop_copy (SmProp *prop) return copy; } +SmProp* +smprop_new_vector (const char *name, + int argc, + char **argv) +{ + SmProp *prop; + int i; + + prop = msm_non_glib_malloc (sizeof (SmProp)); + prop->name = msm_non_glib_strdup (name); + prop->type = msm_non_glib_strdup (SmLISTofARRAY8); + prop->num_vals = argc; + prop->vals = msm_non_glib_malloc (sizeof (SmPropValue) * prop->num_vals); + i = 0; + while (i < argc) + { + prop->vals[i].length = strlen (argv[i]); + prop->vals[i].value = msm_non_glib_strdup (argv[i]); + + ++i; + } + return prop; +} + +SmProp* +smprop_new_string (const char *name, + const char *str, + int len) +{ + SmProp *prop; + + if (len < 0) + len = strlen (str); + + prop = msm_non_glib_malloc (sizeof (SmProp)); + prop->name = msm_non_glib_strdup (name); + prop->type = msm_non_glib_strdup (SmARRAY8); + + prop->num_vals = 1; + prop->vals = msm_non_glib_malloc (sizeof (SmPropValue) * prop->num_vals); + + prop->vals[0].length = len; + prop->vals[0].value = msm_non_glib_malloc (len); + memcpy (prop->vals[0].value, str, len); + + return prop; +} + +SmProp* +smprop_new_card8 (const char *name, + int value) +{ + SmProp *prop; + + prop = msm_non_glib_malloc (sizeof (SmProp)); + prop->name = msm_non_glib_strdup (name); + prop->type = msm_non_glib_strdup (SmARRAY8); + + prop->num_vals = 1; + prop->vals = msm_non_glib_malloc (sizeof (SmPropValue) * prop->num_vals); + + prop->vals[0].length = 1; + prop->vals[0].value = msm_non_glib_malloc (1); + (* (char*) prop->vals[0].value) = (char) value; + + return prop; +} diff --git a/src/msm/props.h b/src/msm/props.h index 92a05f944..09f213fc6 100644 --- a/src/msm/props.h +++ b/src/msm/props.h @@ -41,6 +41,24 @@ gboolean proplist_find_vector (GList *list, int *argcp, char ***argvp); +GList* proplist_replace (GList *list, + SmProp *new_prop); + +GList* proplist_delete (GList *list, + const char *name); + +GList* proplist_replace_card8 (GList *list, + const char *name, + int value); +GList* proplist_replace_string (GList *list, + const char *name, + const char *str, + int len); +GList* proplist_replace_vector (GList *list, + const char *name, + int argc, + char **argv); + gboolean smprop_get_card8 (SmProp *prop, int *result); gboolean smprop_get_string (SmProp *prop, @@ -49,6 +67,16 @@ gboolean smprop_get_vector (SmProp *prop, int *argcp, char ***argvp); +SmProp* smprop_new_card8 (const char *name, + int value); +SmProp* smprop_new_string (const char *name, + const char *str, + int len); +SmProp* smprop_new_vector (const char *name, + int argc, + char **argv); + + SmProp* smprop_copy (SmProp *prop); #endif diff --git a/src/msm/session.c b/src/msm/session.c index 492d0c30f..9bf995f0d 100644 --- a/src/msm/session.c +++ b/src/msm/session.c @@ -30,6 +30,7 @@ #include #include #include +#include #include @@ -38,7 +39,7 @@ typedef struct _MsmSavedClient MsmSavedClient; struct _MsmSavedClient { char *id; - + GList *properties; }; struct _MsmSession @@ -71,6 +72,9 @@ static MsmSession* recover_failed_session (MsmSession *session, static gboolean parse_session_file (MsmSession *session, GError **error); +static char* decode_text_from_utf8 (const char *text); +static char* encode_text_as_utf8 (const char *text); + void msm_session_clear (MsmSession *session) { @@ -106,8 +110,10 @@ msm_session_client_id_known (MsmSession *session, void msm_session_launch (MsmSession *session) { - - + system ("xclock &"); + system ("xclock &"); + system ("xclock &"); + system ("xterm &"); } MsmSavedClient* @@ -118,7 +124,8 @@ saved_new (void) saved = g_new (MsmSavedClient, 1); saved->id = NULL; - + saved->properties = NULL; + return saved; } @@ -344,12 +351,201 @@ msm_session_get_failsafe (void) return msm_session_get_for_filename (_("Failsafe"), "Failsafe.session"); } +static void +write_proplist (FILE *fp, + GList *properties) +{ + GList *tmp; + + tmp = properties; + while (tmp != NULL) + { + SmProp *prop = tmp->data; + char *name_encoded; + char *type_encoded; + + name_encoded = encode_text_as_utf8 (prop->name); + type_encoded = encode_text_as_utf8 (prop->type); + + fprintf (fp, " \n", + name_encoded, type_encoded); + + g_free (name_encoded); + g_free (type_encoded); + + if (strcmp (prop->type, SmCARD8) == 0) + { + int val = 0; + smprop_get_card8 (prop, &val); + fprintf (fp, " %d\n", val); + } + else if (strcmp (prop->type, SmARRAY8) == 0) + { + char *str = NULL; + char *encoded = NULL; + smprop_get_string (prop, &str); + if (str) + encoded = encode_text_as_utf8 (str); + if (encoded) + fprintf (fp, " %s\n", encoded); + + g_free (encoded); + g_free (str); + } + else if (strcmp (prop->type, SmLISTofARRAY8) == 0) + { + char **vec; + int vec_len; + int i; + + vec = NULL; + vec_len = 0; + + smprop_get_vector (prop, &vec_len, &vec); + + i = 0; + while (i < vec_len) + { + char *encoded; + + encoded = encode_text_as_utf8 (vec[i]); + + fprintf (fp, " %s\n", encoded); + + g_free (encoded); + + ++i; + } + + g_strfreev (vec); + } + else + { + msm_warning (_("Not saving unknown property type '%s'\n"), + prop->type); + } + + fputs (" \n", fp); + + tmp = tmp->next; + } +} + void msm_session_save (MsmSession *session, MsmServer *server) { + /* We save to a secondary file then copy over, to handle + * out-of-disk-space robustly + */ + int new_fd; + char *new_filename; + char *error; + FILE *fp; + error = NULL; + new_fd = -1; + + new_filename = g_strconcat (session->full_filename, ".new", NULL); + new_fd = open (session->full_filename, O_RDWR | O_CREAT | O_EXCL, 0700); + if (new_fd < 0) + { + error = g_strdup_printf (_("Failed to open '%s': %s\n"), + new_filename, g_strerror (errno)); + goto out; + } + if (lock_entire_file (new_fd) < 0) + { + error = g_strdup_printf (_("Failed to lock file '%s': %s"), + new_filename, + g_strerror (errno)); + goto out; + } + + fp = fdopen (new_fd, "w"); + if (fp == NULL) + { + error = g_strdup_printf (_("Failed to write to new session file '%s': %s"), + new_filename, g_strerror (errno)); + goto out; + } + + fputs ("\n", fp); + + { + GList *tmp; + tmp = session->clients; + while (tmp != NULL) + { + MsmSavedClient *saved = tmp->data; + char *encoded; + + encoded = encode_text_as_utf8 (saved->id); + + fprintf (fp, " \n", + encoded); + + g_free (encoded); + + write_proplist (fp, saved->properties); + + fputs (" \n", fp); + + tmp = tmp->next; + } + } + + fputs ("\n", fp); + + if (ferror (fp)) + { + error = g_strdup_printf (_("Error writing new session file '%s': %s"), + new_filename, g_strerror (errno)); + fclose (fp); + goto out; + } + + if (fclose (fp) < 0) + { + error = g_strdup_printf (_("Failed to close to new session file '%s': %s"), + new_filename, g_strerror (errno)); + goto out; + } + + if (rename (new_filename, session->full_filename) < 0) + { + error = g_strdup_printf (_("Failed to replace the old session file '%s' with the new session contents in the temporary file '%s': %s"), + session->full_filename, + new_filename, g_strerror (errno)); + goto out; + } + + + + out: + g_free (new_filename); + + if (error) + { + if (new_fd >= 0) + close (new_fd); + } + else + { + if (session->lock_fd >= 0) + close (session->lock_fd); + session->lock_fd = new_fd; + set_close_on_exec (new_fd); + } +} + +static void +add_details_to_dialog (GtkDialog *dialog, + const char *details) +{ + + } static MsmSession* @@ -368,21 +564,43 @@ recover_failed_session (MsmSession *session, case MSM_SESSION_FAILURE_OPENING_FILE: message = g_strdup_printf (_("Could not open the session \"%s.\""), session->name); + /* FIXME recovery options: + * - give up and exit; something pathological is going on + * - choose another session? + * - use default session in read-only mode? + * - open xterm to repair the problem, then try again (experts only) + */ break; case MSM_SESSION_FAILURE_LOCKING: message = g_strdup_printf (_("You are already logged in elsewhere, using the session \"%s.\" You can only use a session from one location at a time."), session->name); + /* FIXME recovery options: + * - log in anyhow, with possible weirdness + * - try again (after logging out the other session) + * - choose another session + * - open xterm to repair the problem, then try again (experts only) + */ break; case MSM_SESSION_FAILURE_BAD_FILE: message = g_strdup_printf (_("The session file for session \"%s\" appears to be invalid or corrupted."), session->name); + /* FIXME recovery options: + * - revert session to defaults + * - choose another session + * - open xterm to repair the problem, then try again (experts only) + */ break; case MSM_SESSION_FAILURE_EMPTY: message = g_strdup_printf (_("The session \"%s\" contains no applications."), session->name); + /* FIXME recovery options: + * - put default applications in the session + * - choose another session + * - open xterm to repair the problem, then try again (experts only) + */ break; } @@ -392,6 +610,9 @@ recover_failed_session (MsmSession *session, GTK_BUTTONS_CLOSE, message); + gtk_window_set_position (GTK_WINDOW (dialog), GTK_WIN_POS_CENTER); + add_details_to_dialog (GTK_DIALOG (dialog), details); + g_free (message); gtk_dialog_run (GTK_DIALOG (dialog)); @@ -445,3 +666,46 @@ parse_session_file (MsmSession *session, return TRUE; } + + +static char* +encode_text_as_utf8 (const char *text) +{ + /* text can be any encoding, and is nul-terminated. + * we pretend it's Latin-1 and encode as UTF-8 + */ + GString *str; + const char *p; + + str = g_string_new (""); + + p = text; + while (*p) + { + g_string_append_unichar (str, *p); + ++p; + } + + return g_string_free (str, FALSE); +} + +static char* +decode_text_from_utf8 (const char *text) +{ + /* Convert back from the encoded UTF-8 */ + GString *str; + const char *p; + + str = g_string_new (""); + + p = text; + while (*p) + { + /* obviously this barfs if the UTF-8 contains chars > 255 */ + g_string_append_c (str, g_utf8_get_char (p)); + + p = g_utf8_next_char (p); + } + + return g_string_free (str, FALSE); +} diff --git a/src/screen.h b/src/screen.h index 5a347fd55..e81abf603 100644 --- a/src/screen.h +++ b/src/screen.h @@ -30,6 +30,8 @@ /* should investigate changing these to whatever most apps use */ #define META_ICON_WIDTH 32 #define META_ICON_HEIGHT 32 +#define META_MINI_ICON_WIDTH 16 +#define META_MINI_ICON_HEIGHT 16 typedef void (* MetaScreenWindowFunc) (MetaScreen *screen, MetaWindow *window, gpointer user_data); diff --git a/src/session.c b/src/session.c index 00290e691..de65a5131 100644 --- a/src/session.c +++ b/src/session.c @@ -97,8 +97,8 @@ new_ice_connection (IceConn connection, IcePointer client_data, Bool opening, */ GIOChannel *channel; - fcntl (IceConnectionNumber(connection),F_SETFD, - fcntl(IceConnectionNumber(connection),F_GETFD,0) | FD_CLOEXEC); + fcntl (IceConnectionNumber (connection), F_SETFD, + fcntl (IceConnectionNumber (connection), F_GETFD, 0) | FD_CLOEXEC); channel = g_io_channel_unix_new (IceConnectionNumber (connection)); @@ -119,15 +119,15 @@ new_ice_connection (IceConn connection, IcePointer client_data, Bool opening, } } -static IceIOErrorHandler gnome_ice_installed_handler; +static IceIOErrorHandler ice_installed_handler; /* We call any handler installed before (or after) gnome_ice_init but avoid calling the default libICE handler which does an exit() */ static void ice_io_error_handler (IceConn connection) { - if (gnome_ice_installed_handler) - (*gnome_ice_installed_handler) (connection); + if (ice_installed_handler) + (*ice_installed_handler) (connection); } static void @@ -139,11 +139,11 @@ ice_init (void) { IceIOErrorHandler default_handler; - gnome_ice_installed_handler = IceSetIOErrorHandler (NULL); + ice_installed_handler = IceSetIOErrorHandler (NULL); default_handler = IceSetIOErrorHandler (ice_io_error_handler); - if (gnome_ice_installed_handler == default_handler) - gnome_ice_installed_handler = NULL; + if (ice_installed_handler == default_handler) + ice_installed_handler = NULL; IceAddConnectionWatch (new_ice_connection, NULL); @@ -879,11 +879,17 @@ save_state (void) out: if (outfile) { - if (fclose (outfile) != 0) + /* FIXME need a dialog for this */ + if (ferror (outfile)) { meta_warning (_("Error writing session file '%s': %s\n"), session_file, g_strerror (errno)); } + if (fclose (outfile)) + { + meta_warning (_("Error closing session file '%s': %s\n"), + session_file, g_strerror (errno)); + } } g_free (metacity_dir); diff --git a/src/ui.c b/src/ui.c index 48909a639..01b3154b0 100644 --- a/src/ui.c +++ b/src/ui.c @@ -429,6 +429,16 @@ meta_ui_get_default_window_icon (MetaUI *ui) NULL); } +GdkPixbuf* +meta_ui_get_default_mini_icon (MetaUI *ui) +{ + /* FIXME */ + return gtk_widget_render_icon (GTK_WIDGET (ui->frames), + GTK_STOCK_NEW, + GTK_ICON_SIZE_MENU, + NULL); +} + gboolean meta_ui_window_should_not_cause_focus (Display *xdisplay, Window xwindow) diff --git a/src/ui.h b/src/ui.h index 7bfdcb609..9fad03e76 100644 --- a/src/ui.h +++ b/src/ui.h @@ -129,6 +129,7 @@ void meta_ui_push_delay_exposes (MetaUI *ui); void meta_ui_pop_delay_exposes (MetaUI *ui); GdkPixbuf* meta_ui_get_default_window_icon (MetaUI *ui); +GdkPixbuf* meta_ui_get_default_mini_icon (MetaUI *ui); gboolean meta_ui_window_should_not_cause_focus (Display *xdisplay, Window xwindow); diff --git a/src/window.c b/src/window.c index 8af7d0ba5..0e3e82122 100644 --- a/src/window.c +++ b/src/window.c @@ -275,6 +275,7 @@ meta_window_new (MetaDisplay *display, Window xwindow, window->title = NULL; window->icon_name = NULL; window->icon = NULL; + window->mini_icon = NULL; window->desc = g_strdup_printf ("0x%lx", window->xwindow); @@ -689,6 +690,9 @@ meta_window_free (MetaWindow *window) if (window->icon) g_object_unref (G_OBJECT (window->icon)); + + if (window->mini_icon) + g_object_unref (G_OBJECT (window->mini_icon)); g_free (window->sm_client_id); g_free (window->role); @@ -3808,6 +3812,8 @@ update_icon_name (MetaWindow *window) static gboolean find_best_size (gulong *data, int nitems, + int ideal_width, + int ideal_height, int *width, int *height, gulong **start) @@ -3857,7 +3863,7 @@ find_best_size (gulong *data, else { /* work with averages */ - const int ideal_size = META_ICON_WIDTH * META_ICON_HEIGHT; + const int ideal_size = (ideal_width + ideal_height) / 2; int best_size = (best_w + best_h) / 2; int this_size = (w + h) / 2; @@ -3875,7 +3881,7 @@ find_best_size (gulong *data, else if (best_size > ideal_size && this_size >= ideal_size && this_size < best_size) - replace = TRUE; + replace = TRUE; } if (replace) @@ -3900,11 +3906,46 @@ find_best_size (gulong *data, return FALSE; } +static void +argbdata_to_pixdata (gulong *argb_data, int len, guchar **pixdata) +{ + guchar *p; + int i; + + *pixdata = g_new (guchar, len * 4); + p = *pixdata; + + /* One could speed this up a lot. */ + i = 0; + while (i < len) + { + guint argb; + guint rgba; + + argb = argb_data[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; + } +} + static gboolean read_rgb_icon (MetaWindow *window, int *width, int *height, - guchar **pixdata) + guchar **pixdata, + int *mini_width, + int *mini_height, + guchar **mini_pixdata) { Atom type; int format; @@ -3913,9 +3954,9 @@ read_rgb_icon (MetaWindow *window, int result; gulong *data; /* FIXME should be guint? */ gulong *best; - int i; int w, h; - guchar *p; + gulong *best_mini; + int mini_w, mini_h; if (sizeof (gulong) != 4) meta_warning ("%s: Whoops, I think this function may be broken on 64-bit\n", @@ -3940,39 +3981,28 @@ read_rgb_icon (MetaWindow *window, return FALSE; } - if (!find_best_size (data, nitems, &w, &h, &best)) + if (!find_best_size (data, nitems, META_ICON_WIDTH, META_ICON_HEIGHT, + &w, &h, &best)) { XFree (data); return FALSE; } + if (!find_best_size (data, nitems, META_MINI_ICON_WIDTH, META_MINI_ICON_HEIGHT, + &mini_w, &mini_h, &best_mini)) + { + XFree (data); + return FALSE; + } + *width = w; *height = h; - *pixdata = g_new (guchar, w * h * 4); - p = *pixdata; + *mini_width = mini_w; + *mini_height = mini_h; - /* 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; - } + argbdata_to_pixdata (best, w * h, pixdata); + argbdata_to_pixdata (best_mini, mini_w * mini_h, mini_pixdata); XFree (data); @@ -3987,6 +4017,12 @@ clear_icon (MetaWindow *window) g_object_unref (G_OBJECT (window->icon)); window->icon = NULL; } + + if (window->mini_icon) + { + g_object_unref (G_OBJECT (window->mini_icon)); + window->mini_icon = NULL; + } } static void @@ -3997,7 +4033,8 @@ free_pixels (guchar *pixels, gpointer data) static void replace_icon (MetaWindow *window, - GdkPixbuf *unscaled) + GdkPixbuf *unscaled, + GdkPixbuf *unscaled_mini) { if (gdk_pixbuf_get_width (unscaled) != META_ICON_WIDTH || gdk_pixbuf_get_height (unscaled) != META_ICON_HEIGHT) @@ -4015,6 +4052,23 @@ replace_icon (MetaWindow *window, g_object_ref (G_OBJECT (unscaled)); window->icon = unscaled; } + + if (gdk_pixbuf_get_width (unscaled_mini) != META_MINI_ICON_WIDTH || + gdk_pixbuf_get_height (unscaled_mini) != META_MINI_ICON_HEIGHT) + { + /* FIXME should keep aspect ratio, but for now assuming + * a square source icon + */ + window->mini_icon = gdk_pixbuf_scale_simple (unscaled_mini, + META_MINI_ICON_WIDTH, + META_MINI_ICON_HEIGHT, + GDK_INTERP_BILINEAR); + } + else + { + g_object_ref (G_OBJECT (unscaled_mini)); + window->mini_icon = unscaled_mini; + } } static void @@ -4149,7 +4203,7 @@ try_pixmap_and_mask (MetaWindow *window, if (unscaled) { - replace_icon (window, unscaled); + replace_icon (window, unscaled, unscaled); g_object_unref (G_OBJECT (unscaled)); return TRUE; } @@ -4162,18 +4216,22 @@ update_icon (MetaWindow *window, gboolean reload_rgb_icon) { - if (FALSE && reload_rgb_icon) + if (reload_rgb_icon) { guchar *pixdata; int w, h; + guchar *mini_pixdata; + int mini_w, mini_h; pixdata = NULL; - if (read_rgb_icon (window, &w, &h, &pixdata)) + if (read_rgb_icon (window, &w, &h, &pixdata, + &mini_w, &mini_h, &mini_pixdata)) { GdkPixbuf *unscaled; + GdkPixbuf *unscaled_mini; - meta_verbose ("successfully read RGBA icon from _NET_WM_ICON, using w = %d h = %d\n", w, h); + meta_verbose ("successfully read RGBA icon from _NET_WM_ICON, using w = %d h = %d mini_w = %d mini_h = %d\n", w, h, mini_w, mini_h); window->using_rgb_icon = TRUE; @@ -4185,9 +4243,18 @@ update_icon (MetaWindow *window, free_pixels, NULL); - replace_icon (window, unscaled); + unscaled_mini = gdk_pixbuf_new_from_data (mini_pixdata, + GDK_COLORSPACE_RGB, + TRUE, + 8, + mini_w, mini_h, mini_w * 4, + free_pixels, + NULL); + + replace_icon (window, unscaled, unscaled_mini); g_object_unref (G_OBJECT (unscaled)); + g_object_unref (G_OBJECT (unscaled_mini)); return Success; } @@ -4237,7 +4304,10 @@ update_icon (MetaWindow *window, /* Fallback to a default icon */ 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); + window->mini_icon = meta_ui_get_default_mini_icon (window->screen->ui); + } return Success; } diff --git a/src/window.h b/src/window.h index 6eab69911..9a23a4f1f 100644 --- a/src/window.h +++ b/src/window.h @@ -55,6 +55,7 @@ struct _MetaWindow char *icon_name; GdkPixbuf *icon; + GdkPixbuf *mini_icon; MetaWindowType type; Atom type_atom;