This commit is contained in:
rhp 2001-07-28 06:11:15 +00:00
parent 857757b221
commit 46d224f5ae
2 changed files with 278 additions and 48 deletions

89
README
View File

@ -6,6 +6,23 @@ on UNIX keyboards.
The first release of Metacity is version 2.3. Metacity has no need for The first release of Metacity is version 2.3. Metacity has no need for
your petty hangups about version numbers. your petty hangups about version numbers.
COMPILING METACITY
===
You need GTK+ 1.3.x (to become 2.0). At the moment CVS HEAD is
required. Once 1.3.7 is available that will probably work. Metacity is
a fairly trivial 6000-line C program, so once you get GTK+ built it
should be no problem to build Metacity.
REPORTING BUGS AND SUBMITTING PATCHES
===
Report new bugs to hp@redhat.com for now. Will switch to Bugzilla
sometime probably.
Feel free to send patches too; Metacity is really small and simple, so
if you find a bug or want to add a feature it should be pretty easy.
METACITY FEATURES METACITY FEATURES
=== ===
@ -69,8 +86,9 @@ METACITY FEATURES
- Session management: - Session management:
Metacity connects to the session manager and will set itself up Metacity connects to the session manager and will set itself up to
to be respawned. be respawned. It theoretically restores sizes/positions/workspace
for session-aware applications.
- Here is an example of how you can configure the Metacity - Here is an example of how you can configure the Metacity
window border appearance in ~/.gtkrc-2.0: window border appearance in ~/.gtkrc-2.0:
@ -115,16 +133,18 @@ METACITY BUGS, NON-FEATURES, AND CAVEATS
- Metacity does not have any way to unminimize a window. So - Metacity does not have any way to unminimize a window. So
clicking the minimize button is sort of a bad idea. clicking the minimize button is sort of a bad idea.
(If you had a WM-spec-compliant tasklist, it would work
for unminimization.)
- Metacity uses the new window manager spec, but only random bits - Metacity uses the new window manager spec, but only random bits of
of the old GNOME spec. It correctly advertises exactly which the old GNOME spec. It correctly advertises exactly which parts of
parts of the GNOME spec it supports, but it does not support the GNOME spec it supports, but it does not support enough of it to
enough of it to make the GNOME task list and desk guide happy, make the GNOME task list and desk guide happy, and they do not
and they do not support the new spec. I don't want anyone to support the new spec. I don't want anyone to spend time sending me
spend time sending me patches to support the old GNOME spec; patches to support the old GNOME spec in Metacity; instead, send
instead, send patches to the task list and desk guide to support patches to the task list and desk guide to support the new spec. As
the new spec. As far as I know, Metacity does support enough far as I know, Metacity does support enough of the new spec to
of the new spec to allow a working tasklist and pager. allow a working tasklist and pager.
Upshot: task list and desk guide DO NOT WORK with Metacity. Upshot: task list and desk guide DO NOT WORK with Metacity.
@ -134,12 +154,14 @@ METACITY BUGS, NON-FEATURES, AND CAVEATS
instead of Alt as the main keybinding shortcut, if super/hyper instead of Alt as the main keybinding shortcut, if super/hyper
exist, and then keyboards with a windows key can use that for exist, and then keyboards with a windows key can use that for
WM functions and Alt for application shortcuts. WM functions and Alt for application shortcuts.
We'd fall back to Alt if no other suitable modifier existed.
- Cycling windows with Alt-Tab is flickery, AFAIK because - Cycling windows with Alt-Tab is flickery, AFAIK because
Metacity passes the entire window stack to XRestackWindows() Metacity passes the entire window stack to XRestackWindows()
every time you restack. Instead it should probably only restack every time you restack. Instead it should probably only restack
windows that have changed their stacking with respect to one windows that have changed their stacking with respect to one
another. another. (But sometimes I don't see the flicker, so
I'm not sure.)
- Various operations, such as minimize, maximize, etc., should - Various operations, such as minimize, maximize, etc., should
have simple animations to make them clearer to users. have simple animations to make them clearer to users.
@ -151,8 +173,13 @@ METACITY BUGS, NON-FEATURES, AND CAVEATS
as described in the ICCCM. But then, most other window managers as described in the ICCCM. But then, most other window managers
don't handle this correctly either. don't handle this correctly either.
- I know there's at least one race condition involving rapidly - There are probably other ICCCM-compliance issues.
created and destroyed windows that will crash Metacity.
- I know there's at least one race condition involving rapidly
created and destroyed windows that will crash Metacity. I think a
small test app that did a lot of rapid show/hide on a GtkWindow,
in combination with the METACITY_SYNC environment variable and
Metacity in a debugger, would turn this up quickly.
- Window placement is always cascade for now; I want to implement - Window placement is always cascade for now; I want to implement
"first fit, falling back to cascade if no fit." "first fit, falling back to cascade if no fit."
@ -186,21 +213,18 @@ METACITY BUGS, NON-FEATURES, AND CAVEATS
locale. I assume the window titles should be right-justified; locale. I assume the window titles should be right-justified;
should the window controls also be flipped? should the window controls also be flipped?
COMPILING METACITY - Need keyboard shortcuts for hide/show desktop (minimize/unminimize
=== all windows temporarily, basically, and focus desktop); also
for focusing dock windows (though since current GNOME panel has
no useful keynav, this doesn't get you far at the moment).
You need GTK+ 1.3.x (to become 2.0). See configure.in for the exact - Resize menu item doesn't do anything. It's intended to enter
version you need. Metacity is a fairly trivial 6000-line C program, so resize-with-the-keyboard mode, similar to Move menu item.
once you get GTK+ built it should be no problem to build Metacity.
REPORTING BUGS AND SUBMITTING PATCHES - I haven't set up the po subdir and translations, mostly because
=== autotools seem to get all confused if you set these up but
don't actually have any translations. So if someone translates
Report new bugs to hp@redhat.com for now. Will switch to Bugzilla something we can set it up.
sometime probably.
Feel free to send patches too; Metacity is really small and simple, so
if you find a bug or want to add a feature it should be pretty easy.
FAQ FAQ
=== ===
@ -267,8 +291,8 @@ A: Metacity only stores sizes/positions for apps that are session
bunch of new windows appear. But it can't distinguish between bunch of new windows appear. But it can't distinguish between
windows that were stored in your session, or windows you just windows that were stored in your session, or windows you just
launched after logging in. If Metacity tried to guess that a window launched after logging in. If Metacity tried to guess that a window
was from the session, it could e.g. maximize a dialog, or put a was from the session, it could e.g. end up maximizing a dialog, or
window you just launched on another desktop or in a weird put a window you just launched on another desktop or in a weird
place. And in fact I see a lot of bugs like this in window managers place. And in fact I see a lot of bugs like this in window managers
that try to handle non-session-aware apps. that try to handle non-session-aware apps.
@ -287,10 +311,9 @@ A: Metacity only stores sizes/positions for apps that are session
ftp://ftp.x.org/pub/R6.4/xc/doc/hardcopy/SM/xsmp.PS.gz ftp://ftp.x.org/pub/R6.4/xc/doc/hardcopy/SM/xsmp.PS.gz
ftp://ftp.x.org:21/pub/R6.4/xc/doc/hardcopy/SM/SMlib.PS.gz ftp://ftp.x.org:21/pub/R6.4/xc/doc/hardcopy/SM/SMlib.PS.gz
See also the ICCCM section on SM. For GNOME apps, use See also the ICCCM section on SM. For GNOME apps, use the
the GnomeClient object. For a simple coding example using GnomeClient object. For a simple example of using libSM directly,
libSM directly, twm/session.c in the twm source code is twm/session.c in the twm source code is pretty easy to understand.
pretty easy to understand.
Q: How about adding viewports in addition to workspaces? Q: How about adding viewports in addition to workspaces?

View File

@ -585,6 +585,116 @@ window_type_from_string (const char *str)
return META_WINDOW_NORMAL; return META_WINDOW_NORMAL;
} }
static const char*
window_gravity_to_string (int gravity)
{
switch (gravity)
{
case NorthWestGravity:
return "NorthWestGravity";
break;
case NorthGravity:
return "NorthGravity";
break;
case NorthEastGravity:
return "NorthEastGravity";
break;
case WestGravity:
return "WestGravity";
break;
case CenterGravity:
return "CenterGravity";
break;
case EastGravity:
return "EastGravity";
break;
case SouthWestGravity:
return "SouthWestGravity";
break;
case SouthGravity:
return "SouthGravity";
break;
case SouthEastGravity:
return "SouthEastGravity";
break;
case StaticGravity:
return "StaticGravity";
break;
default:
return "NorthWestGravity";
break;
}
}
static int
window_gravity_from_string (const char *str)
{
if (strcmp (str, "NorthWestGravity") == 0)
return NorthWestGravity;
else if (strcmp (str, "NorthGravity") == 0)
return NorthGravity;
else if (strcmp (str, "NorthEastGravity") == 0)
return NorthEastGravity;
else if (strcmp (str, "WestGravity") == 0)
return WestGravity;
else if (strcmp (str, "CenterGravity") == 0)
return CenterGravity;
else if (strcmp (str, "EastGravity") == 0)
return EastGravity;
else if (strcmp (str, "SouthWestGravity") == 0)
return SouthWestGravity;
else if (strcmp (str, "SouthGravity") == 0)
return SouthGravity;
else if (strcmp (str, "SouthEastGravity") == 0)
return SouthEastGravity;
else if (strcmp (str, "StaticGravity") == 0)
return StaticGravity;
else
return NorthWestGravity;
}
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);
}
static void static void
save_state (void) save_state (void)
{ {
@ -651,10 +761,6 @@ save_state (void)
* child elements are the saved state to be applied. * child elements are the saved state to be applied.
* *
*/ */
/* FIXME we are putting non-UTF-8 in here. */
meta_warning ("FIXME Saving session ID, class, name, etc. pretending that they are valid UTF-8, but no such thing is necessarily true");
fprintf (outfile, "<metacity_session id=\"%s\">\n", fprintf (outfile, "<metacity_session id=\"%s\">\n",
client_id); client_id);
@ -676,18 +782,41 @@ save_state (void)
if (window->sm_client_id) if (window->sm_client_id)
{ {
char *sm_client_id;
char *res_class;
char *res_name;
char *role;
/* client id, class, name, role are not expected to be
* in UTF-8 (I think they are in XPCS which is Latin-1?
* in practice they are always ascii though.)
*/
sm_client_id = encode_text_as_utf8 (window->sm_client_id);
res_class = window->res_class ?
encode_text_as_utf8 (window->res_class) : NULL;
res_name = window->res_name ?
encode_text_as_utf8 (window->res_name) : NULL;
role = window->role ?
encode_text_as_utf8 (window->role) : NULL;
meta_verbose ("Saving session managed window %s, client ID '%s'\n", meta_verbose ("Saving session managed window %s, client ID '%s'\n",
window->desc, window->sm_client_id); window->desc, window->sm_client_id);
fprintf (outfile, fprintf (outfile,
" <window id=\"%s\" class=\"%s\" name=\"%s\" title=\"%s\" role=\"%s\" type=\"%s\">\n", " <window id=\"%s\" class=\"%s\" name=\"%s\" title=\"%s\" role=\"%s\" type=\"%s\">\n",
window->sm_client_id, sm_client_id,
window->res_class ? window->res_class : "", res_class ? res_class : "",
window->res_name ? window->res_name : "", res_name ? res_name : "",
window->title ? window->title : "", window->title ? window->title : "",
window->role ? window->role : "", role ? role : "",
window_type_to_string (window->type)); window_type_to_string (window->type));
g_free (sm_client_id);
g_free (res_class);
g_free (res_name);
g_free (role);
/* Sticky */ /* Sticky */
if (window->on_all_workspaces) if (window->on_all_workspaces)
fputs (" <sticky/>\n", outfile); fputs (" <sticky/>\n", outfile);
@ -706,6 +835,17 @@ save_state (void)
w = w->next; w = w->next;
} }
} }
/* Gravity */
{
int x, y, w, h;
meta_window_get_geometry (window, &x, &y, &w, &h);
fprintf (outfile,
" <geometry x=\"%d\" y=\"%d\" width=\"%d\" height=\"%d\" gravity=\"%s\"/>\n",
x, y, w, h,
window_gravity_to_string (window->size_hints.win_gravity));
}
fputs (" </window>\n", outfile); fputs (" </window>\n", outfile);
} }
@ -813,6 +953,10 @@ load_state (const char *previous_id)
return; return;
} }
meta_verbose ("Parsing saved session file %s\n", session_file);
g_free (session_file);
session_file = NULL;
parse_data.info = NULL; parse_data.info = NULL;
context = g_markup_parse_context_new (&metacity_session_parser, context = g_markup_parse_context_new (&metacity_session_parser,
@ -835,13 +979,16 @@ load_state (const char *previous_id)
goto out; goto out;
error: error:
meta_warning (_("Failed to parse saved session file: %s\n"), meta_warning (_("Failed to parse saved session file: %s\n"),
error->message); error->message);
g_error_free (error); g_error_free (error);
if (parse_data.info)
session_info_free (parse_data.info);
out: out:
g_free (text); g_free (text);
} }
@ -889,17 +1036,17 @@ start_element_handler (GMarkupParseContext *context,
if (strcmp (name, "id") == 0) if (strcmp (name, "id") == 0)
{ {
if (*val) if (*val)
pd->info->id = g_strdup (val); pd->info->id = decode_text_from_utf8 (val);
} }
else if (strcmp (name, "class") == 0) else if (strcmp (name, "class") == 0)
{ {
if (*val) if (*val)
pd->info->res_class = g_strdup (val); pd->info->res_class = decode_text_from_utf8 (val);
} }
else if (strcmp (name, "name") == 0) else if (strcmp (name, "name") == 0)
{ {
if (*val) if (*val)
pd->info->res_name = g_strdup (val); pd->info->res_name = decode_text_from_utf8 (val);
} }
else if (strcmp (name, "title") == 0) else if (strcmp (name, "title") == 0)
{ {
@ -909,7 +1056,7 @@ start_element_handler (GMarkupParseContext *context,
else if (strcmp (name, "role") == 0) else if (strcmp (name, "role") == 0)
{ {
if (*val) if (*val)
pd->info->role = g_strdup (val); pd->info->role = decode_text_from_utf8 (val);
} }
else if (strcmp (name, "type") == 0) else if (strcmp (name, "type") == 0)
{ {
@ -970,8 +1117,63 @@ start_element_handler (GMarkupParseContext *context,
} }
else if (strcmp (element_name, "geometry") == 0) else if (strcmp (element_name, "geometry") == 0)
{ {
int i;
pd->info->geometry_set = TRUE;
i = 0;
while (attribute_names[i])
{
const char *name;
const char *val;
name = attribute_names[i];
val = attribute_values[i];
if (strcmp (name, "x") == 0)
{
if (*val)
pd->info->rect.x = atoi (val);
}
else if (strcmp (name, "y") == 0)
{
if (*val)
pd->info->rect.y = atoi (val);
}
else if (strcmp (name, "width") == 0)
{
if (*val)
pd->info->rect.width = atoi (val);
}
else if (strcmp (name, "height") == 0)
{
if (*val)
pd->info->rect.height = atoi (val);
}
else if (strcmp (name, "gravity") == 0)
{
if (*val)
pd->info->gravity = window_gravity_from_string (val);
}
else
{
g_set_error (error,
G_MARKUP_ERROR,
G_MARKUP_ERROR_UNKNOWN_ATTRIBUTE,
_("Unknown attribute %s on <geometry> element"),
name);
return;
}
++i;
}
meta_verbose ("Loaded geometry %d,%d %dx%d gravity %s\n",
pd->info->rect.x,
pd->info->rect.y,
pd->info->rect.width,
pd->info->rect.height,
window_gravity_to_string (pd->info->gravity));
} }
else else
{ {
@ -1045,9 +1247,12 @@ get_possible_matches (MetaWindow *window)
/* Get all windows with this client ID */ /* Get all windows with this client ID */
GSList *retval; GSList *retval;
GSList *tmp; GSList *tmp;
gboolean ignore_client_id;
retval = NULL; retval = NULL;
ignore_client_id = g_getenv ("METACITY_DEBUG_SM") != NULL;
tmp = window_info_list; tmp = window_info_list;
while (tmp != NULL) while (tmp != NULL)
{ {
@ -1055,7 +1260,8 @@ get_possible_matches (MetaWindow *window)
info = tmp->data; info = tmp->data;
if (both_null_or_matching (info->id, window->sm_client_id) && if ((ignore_client_id ||
both_null_or_matching (info->id, window->sm_client_id)) &&
both_null_or_matching (info->res_class, window->res_class) && both_null_or_matching (info->res_class, window->res_class) &&
both_null_or_matching (info->res_name, window->res_name) && both_null_or_matching (info->res_name, window->res_name) &&
both_null_or_matching (info->role, window->role)) both_null_or_matching (info->role, window->role))
@ -1216,6 +1422,7 @@ session_info_new (void)
info = g_new0 (MetaWindowSessionInfo, 1); info = g_new0 (MetaWindowSessionInfo, 1);
info->type = META_WINDOW_NORMAL; info->type = META_WINDOW_NORMAL;
info->gravity = NorthWestGravity;
return info; return info;
} }