This commit is contained in:
rhp 2001-06-03 01:33:27 +00:00
parent e47c4d16a2
commit ce7c53bf1a
19 changed files with 1663 additions and 225 deletions

View File

@ -62,6 +62,9 @@ PKG_CHECK_MODULES(METACITY, $PANGO_PACKAGES)
# Check for shaped window extension
AC_CHECK_LIB(Xext, XShapeCombineMask, AC_DEFINE(HAVE_SHAPE_EXT),,$METACITY_LIBS)
HOST_ALIAS=$host_alias
AC_SUBST(HOST_ALIAS)
AC_OUTPUT([
Makefile
intl/Makefile

View File

@ -1,7 +1,7 @@
SUBDIRS=uislave
INCLUDES=@METACITY_CFLAGS@
INCLUDES=@METACITY_CFLAGS@ -DMETACITY_LIBEXECDIR=\"$(libexecdir)\" -DHOST_ALIAS=\"@HOST_ALIAS@\"
metacity_SOURCES= \
api.c \

View File

@ -24,14 +24,18 @@
#include "main.h"
#include "screen.h"
#include "window.h"
#include "frame.h"
static GSList *all_displays = NULL;
static void meta_spew_event (MetaDisplay *display,
XEvent *event);
static void event_queue_callback (MetaEventQueue *queue,
XEvent *event,
gpointer data);
static Window event_get_modified_window (MetaDisplay *display,
XEvent *event);
static gint
@ -63,7 +67,7 @@ meta_display_open (const char *name)
GSList *screens;
GSList *tmp;
int i;
char *atom_names[] = { "_NET_WM_NAME" };
char *atom_names[] = { "_NET_WM_NAME", "WM_PROTOCOLS", "WM_TAKE_FOCUS", "WM_DELETE_WINDOW" };
Atom atoms[G_N_ELEMENTS(atom_names)];
meta_verbose ("Opening display '%s'\n", XDisplayName (name));
@ -82,6 +86,10 @@ meta_display_open (const char *name)
display = g_new (MetaDisplay, 1);
/* here we use XDisplayName which is what the user
* probably put in, vs. DisplayString(display) which is
* canonicalized by XOpenDisplay()
*/
display->name = g_strdup (XDisplayName (name));
display->xdisplay = xdisplay;
display->error_traps = NULL;
@ -277,27 +285,27 @@ event_queue_callback (MetaEventQueue *queue,
{
MetaWindow *window;
MetaDisplay *display;
gboolean is_root;
Window modified;
display = data;
if (dump_events)
meta_spew_event (display, event);
is_root = meta_display_screen_for_root (display, event->xany.window) != NULL;
modified = event_get_modified_window (display, event);
if (modified != None)
window = meta_display_lookup_x_window (display, modified);
else
window = NULL;
if (!is_root)
if (window &&
window->frame &&
modified == window->frame->xwindow)
{
if (window == NULL)
window = meta_display_lookup_x_window (display, event->xany.window);
if (window != NULL)
{
if (meta_window_event (window, event))
meta_frame_event (window->frame, event);
return;
}
}
switch (event->type)
{
@ -332,28 +340,59 @@ event_queue_callback (MetaEventQueue *queue,
case CreateNotify:
break;
case DestroyNotify:
if (window)
meta_window_free (window); /* Unmanage destroyed window */
break;
case UnmapNotify:
if (window)
meta_window_free (window); /* Unmanage withdrawn window */
break;
case MapNotify:
break;
case MapRequest:
if (is_root && !event->xmap.override_redirect)
{
/* Window requested mapping. Manage it if we haven't. Note that
* meta_window_new() can return NULL
*/
window = meta_display_lookup_x_window (display,
event->xmaprequest.window);
if (window == NULL)
window = meta_window_new (display, event->xmaprequest.window);
}
break;
case ReparentNotify:
break;
case ConfigureNotify:
if (event->xconfigure.override_redirect)
{
/* Unmanage it, override_redirect was toggled on?
* Can this happen?
*/
meta_window_free (window);
}
break;
case ConfigureRequest:
/* This comment and code is found in both twm and fvwm */
/*
* According to the July 27, 1988 ICCCM draft, we should ignore size and
* position fields in the WM_NORMAL_HINTS property when we map a window.
* Instead, we'll read the current geometry. Therefore, we should respond
* to configuration requests for windows which have never been mapped.
*/
if (window == NULL)
{
unsigned int xwcm;
XWindowChanges xwc;
xwcm = event->xconfigurerequest.value_mask &
(CWX | CWY | CWWidth | CWHeight | CWBorderWidth);
xwc.x = event->xconfigurerequest.x;
xwc.y = event->xconfigurerequest.y;
xwc.width = event->xconfigurerequest.width;
xwc.height = event->xconfigurerequest.height;
xwc.border_width = event->xconfigurerequest.border_width;
XConfigureWindow (display->xdisplay, event->xconfigurerequest.window,
xwcm, &xwc);
}
else
{
meta_window_configure_request (window, event);
}
break;
case GravityNotify:
break;
@ -364,6 +403,8 @@ event_queue_callback (MetaEventQueue *queue,
case CirculateRequest:
break;
case PropertyNotify:
if (window)
meta_window_property_notify (window, event);
break;
case SelectionClear:
break;
@ -382,6 +423,80 @@ event_queue_callback (MetaEventQueue *queue,
}
}
/* Return the window this has to do with, if any, rather
* than the frame or root window that was selecting
* for substructure
*/
static Window
event_get_modified_window (MetaDisplay *display,
XEvent *event)
{
switch (event->type)
{
case KeyPress:
case KeyRelease:
case ButtonPress:
case ButtonRelease:
case MotionNotify:
case EnterNotify:
case LeaveNotify:
case FocusIn:
case FocusOut:
case KeymapNotify:
case Expose:
case GraphicsExpose:
case NoExpose:
case VisibilityNotify:
case ResizeRequest:
case PropertyNotify:
case SelectionClear:
case SelectionRequest:
case SelectionNotify:
case ColormapNotify:
case ClientMessage:
return event->xany.window;
case CreateNotify:
return event->xcreatewindow.window;
case DestroyNotify:
return event->xdestroywindow.window;
case UnmapNotify:
return event->xunmap.window;
case MapNotify:
return event->xmap.window;
case MapRequest:
return event->xmaprequest.window;
case ReparentNotify:
return event->xreparent.window;
case ConfigureNotify:
return event->xconfigure.window;
case ConfigureRequest:
return event->xconfigurerequest.window;
case GravityNotify:
return event->xgravity.window;
case CirculateNotify:
return event->xcirculate.window;
case CirculateRequest:
return event->xcirculaterequest.window;
case MappingNotify:
return None;
default:
return None;
}
}
static void
meta_spew_event (MetaDisplay *display,
XEvent *event)
@ -465,6 +580,14 @@ meta_spew_event (MetaDisplay *display,
break;
case ConfigureRequest:
name = "ConfigureRequest";
extra = g_strdup_printf ("parent: 0x%lx window: 0x%lx x: %d y: %d w: %d h: %d border: %d",
event->xconfigurerequest.parent,
event->xconfigurerequest.window,
event->xconfigurerequest.x,
event->xconfigurerequest.y,
event->xconfigurerequest.width,
event->xconfigurerequest.height,
event->xconfigurerequest.border_width);
break;
case GravityNotify:
name = "GravityNotify";

View File

@ -31,6 +31,7 @@ typedef struct _MetaDisplay MetaDisplay;
typedef struct _MetaFrame MetaFrame;
typedef struct _MetaScreen MetaScreen;
typedef struct _MetaWindow MetaWindow;
typedef struct _MetaUISlave MetaUISlave;
struct _MetaDisplay
{
@ -38,6 +39,9 @@ struct _MetaDisplay
Display *xdisplay;
Atom atom_net_wm_name;
Atom atom_wm_protocols;
Atom atom_wm_take_focus;
Atom atom_wm_delete_window;
/*< private-ish >*/
MetaEventQueue *events;

View File

@ -37,109 +37,145 @@ meta_frame_init_info (MetaFrame *frame,
info->height = frame->rect.height;
}
void
meta_window_ensure_frame (MetaWindow *window)
static void
meta_frame_calc_initial_pos (MetaFrame *frame,
int child_root_x, int child_root_y)
{
MetaWindow *window;
window = frame->window;
switch (window->size_hints.win_gravity)
{
case NorthWestGravity:
frame->rect.x = child_root_x;
frame->rect.y = child_root_y;
break;
case NorthGravity:
frame->rect.x = child_root_x - frame->rect.width / 2;
frame->rect.y = child_root_y;
break;
case NorthEastGravity:
frame->rect.x = child_root_x - frame->rect.width;
frame->rect.y = child_root_y;
break;
case WestGravity:
frame->rect.x = child_root_x;
frame->rect.y = child_root_y - frame->rect.height / 2;
break;
case CenterGravity:
frame->rect.x = child_root_x - frame->rect.width / 2;
frame->rect.y = child_root_y - frame->rect.height / 2;
break;
case EastGravity:
frame->rect.x = child_root_x - frame->rect.width;
frame->rect.y = child_root_y - frame->rect.height / 2;
break;
case SouthWestGravity:
frame->rect.x = child_root_x;
frame->rect.y = child_root_y - frame->rect.height;
break;
case SouthGravity:
frame->rect.x = child_root_x - frame->rect.width / 2;
frame->rect.y = child_root_y - frame->rect.height;
break;
case SouthEastGravity:
frame->rect.x = child_root_x - frame->rect.width;
frame->rect.y = child_root_y - frame->rect.height;
break;
case StaticGravity:
default:
frame->rect.x = child_root_x - frame->child_x;
frame->rect.y = child_root_y - frame->child_y;
break;
}
}
static void
meta_frame_calc_geometry (MetaFrame *frame,
int child_width, int child_height,
MetaFrameGeometry *geomp)
{
MetaFrame *frame;
int child_x, child_y;
unsigned long background_pixel;
XSetWindowAttributes attrs;
MetaFrameInfo info;
MetaFrameGeometry geom;
MetaWindow *window;
if (window->frame)
return;
/* Remember this is called from the constructor
* pre-window-creation.
*/
frame = g_new (MetaFrame, 1);
window = frame->window;
frame->window = window;
/* Fill these in for the theme engine's benefit */
frame->xwindow = None;
frame->rect.width = window->rect.width;
frame->rect.height = window->rect.height;
frame->rect.width = child_width;
frame->rect.height = child_height;
meta_frame_init_info (frame, &info);
if (!frame->theme_acquired)
frame->theme_data = window->screen->engine->acquire_frame (&info);
geom.left_width = 0;
geom.right_width = 0;
geom.top_height = 0;
geom.bottom_height = 0;
geom.background_pixel = BlackPixel (frame->window->display->xdisplay,
frame->window->screen->number);
geom.background_pixel = BlackPixel (window->display->xdisplay,
window->screen->number);
geom.shape_mask = None;
frame->theme_data = window->screen->engine->acquire_frame (&info);
window->screen->engine->fill_frame_geometry (&info, &geom,
frame->theme_data);
child_x = geom.left_width;
child_y = geom.top_height;
frame->child_x = geom.left_width;
frame->child_y = geom.top_height;
frame->rect.width = window->rect.width + geom.left_width + geom.right_width;
frame->rect.height = window->rect.height + geom.top_height + geom.bottom_height;
frame->rect.width = frame->rect.width + geom.left_width + geom.right_width;
frame->rect.height = frame->rect.height + geom.top_height + geom.bottom_height;
background_pixel = geom.background_pixel;
switch (window->size_hints.win_gravity)
{
case NorthWestGravity:
frame->rect.x = window->rect.x;
frame->rect.y = window->rect.y;
break;
case NorthGravity:
frame->rect.x = window->rect.x - frame->rect.width / 2;
frame->rect.y = window->rect.y;
break;
case NorthEastGravity:
frame->rect.x = window->rect.x - frame->rect.width;
frame->rect.y = window->rect.y;
break;
case WestGravity:
frame->rect.x = window->rect.x;
frame->rect.y = window->rect.y - frame->rect.height / 2;
break;
case CenterGravity:
frame->rect.x = window->rect.x - frame->rect.width / 2;
frame->rect.y = window->rect.y - frame->rect.height / 2;
break;
case EastGravity:
frame->rect.x = window->rect.x - frame->rect.width;
frame->rect.y = window->rect.y - frame->rect.height / 2;
break;
case SouthWestGravity:
frame->rect.x = window->rect.x;
frame->rect.y = window->rect.y - frame->rect.height;
break;
case SouthGravity:
frame->rect.x = window->rect.x - frame->rect.width / 2;
frame->rect.y = window->rect.y - frame->rect.height;
break;
case SouthEastGravity:
frame->rect.x = window->rect.x - frame->rect.width;
frame->rect.y = window->rect.y - frame->rect.height;
break;
case StaticGravity:
default:
frame->rect.x = window->rect.x - child_x;
frame->rect.y = window->rect.y - child_y;
break;
*geomp = geom;
}
meta_verbose ("Creating frame %d,%d %dx%d around window 0x%lx %d,%d %dx%d with child position inside frame %d,%d and gravity %d\n",
void
meta_window_ensure_frame (MetaWindow *window)
{
MetaFrame *frame;
XSetWindowAttributes attrs;
MetaFrameGeometry geom;
if (window->frame)
return;
/* Need to fix Pango, it grabs the server */
g_return_if_fail (window->display->server_grab_count == 0);
frame = g_new (MetaFrame, 1);
/* Fill in values that calc_geometry will use */
frame->window = window;
frame->xwindow = None;
frame->theme_acquired = FALSE;
/* This fills in frame->rect as well. */
meta_frame_calc_geometry (frame,
window->rect.width,
window->rect.height,
&geom);
meta_frame_calc_initial_pos (frame, window->rect.x, window->rect.y);
meta_verbose ("Will create frame %d,%d %dx%d around window %s %d,%d %dx%d with child position inside frame %d,%d and gravity %d\n",
frame->rect.x, frame->rect.y,
frame->rect.width, frame->rect.height,
window->xwindow,
window->desc,
window->rect.x, window->rect.y,
window->rect.width, window->rect.height,
child_x, child_y,
frame->child_x, frame->child_y,
window->size_hints.win_gravity);
attrs.background_pixel = background_pixel;
attrs.background_pixel = geom.background_pixel;
attrs.event_mask =
StructureNotifyMask | ExposureMask |
ButtonPressMask | ButtonReleaseMask |
StructureNotifyMask | SubstructureNotifyMask | ExposureMask |
ButtonPressMask | ButtonReleaseMask | OwnerGrabButtonMask |
PointerMotionMask | PointerMotionHintMask;
frame->xwindow = XCreateWindow (window->display->xdisplay,
@ -177,10 +213,14 @@ meta_window_ensure_frame (MetaWindow *window)
XReparentWindow (window->display->xdisplay,
window->xwindow,
frame->xwindow,
child_x,
child_y);
frame->child_x,
frame->child_y);
meta_error_trap_pop (window->display);
/* Update window's location */
window->rect.x = frame->child_x;
window->rect.y = frame->child_y;
/* stick frame to the window */
window->frame = frame;
@ -246,6 +286,90 @@ meta_frame_move (MetaFrame *frame,
root_x, root_y);
}
/* Just a chunk of process_configure_event in window.c,
* moved here since it's the part that deals with
* the frame.
*/
void
meta_frame_child_configure_request (MetaFrame *frame)
{
MetaFrameGeometry geom;
/* This fills in frame->rect as well. */
meta_frame_calc_geometry (frame,
frame->window->size_hints.width,
frame->window->size_hints.height,
&geom);
meta_frame_calc_initial_pos (frame,
frame->window->size_hints.x,
frame->window->size_hints.y);
XMoveResizeWindow (frame->window->display->xdisplay,
frame->xwindow,
frame->rect.x,
frame->rect.y,
frame->rect.width,
frame->rect.height);
}
void
meta_frame_recalc_now (MetaFrame *frame)
{
int old_child_x, old_child_y;
MetaFrameGeometry geom;
XSetWindowAttributes attrs;
old_child_x = frame->child_x;
old_child_y = frame->child_y;
/* This fills in frame->rect as well. */
meta_frame_calc_geometry (frame,
frame->window->rect.width,
frame->window->rect.height,
&geom);
/* See if we need to move the frame to keep child in
* a constant position
*/
if (old_child_x != frame->child_x)
frame->rect.x += (frame->child_x - old_child_x);
if (old_child_y != frame->child_y)
frame->rect.y += (frame->child_y - old_child_y);
XMoveResizeWindow (frame->window->display->xdisplay,
frame->xwindow,
frame->rect.x,
frame->rect.y,
frame->rect.width,
frame->rect.height);
attrs.background_pixel = geom.background_pixel;
XChangeWindowAttributes (frame->window->display->xdisplay,
frame->xwindow,
CWBackPixel,
&attrs);
meta_verbose ("Frame of %s recalculated to %d,%d %d x %d child %d,%d\n",
frame->window->desc, frame->rect.x, frame->rect.y,
frame->rect.width, frame->rect.height,
frame->child_x, frame->child_y);
}
void
meta_frame_queue_recalc (MetaFrame *frame)
{
/* FIXME */
meta_frame_recalc_now (frame);
}
void
meta_frame_queue_draw (MetaFrame *frame)
{
/* FIXME */
}
static void
frame_query_root_pointer (MetaFrame *frame,
int *x, int *y)
@ -283,6 +407,37 @@ frame_get_control (MetaFrame *frame,
frame->theme_data);
}
static void
update_move (MetaFrame *frame)
{
int x, y;
int new_x, new_y;
frame_query_root_pointer (frame, &x, &y);
new_x = frame->rect.x + (x - frame->last_x);
new_y = frame->rect.y + (y - frame->last_y);
frame->last_x = x;
frame->last_y = y;
meta_frame_move (frame, new_x, new_y);
}
static void
update_resize_se (MetaFrame *frame)
{
int x, y;
int new_w, new_h;
frame_query_root_pointer (frame, &x, &y);
new_w = frame->window->rect.width + (x - frame->last_x);
new_h = frame->window->rect.height + (y - frame->last_y);
frame->last_x = x;
frame->last_y = y;
meta_window_resize (frame->window, new_w, new_h);
}
gboolean
meta_frame_event (MetaFrame *frame,
XEvent *event)
@ -323,15 +478,32 @@ meta_frame_event (MetaFrame *frame,
else if (control == META_FRAME_CONTROL_RESIZE_SE &&
event->xbutton.button == 1)
{
/* FIXME begin a resize */
meta_verbose ("Resize control clicked on %s\n",
frame->window->desc);
frame->action = META_FRAME_ACTION_RESIZING_SE;
frame->last_x = event->xbutton.x_root;
frame->last_y = event->xbutton.y_root;
frame->start_button = event->xbutton.button;
}
}
break;
case ButtonRelease:
if (event->xbutton.button == frame->start_button)
{
switch (frame->action)
{
case META_FRAME_ACTION_MOVING:
update_move (frame);
break;
case META_FRAME_ACTION_RESIZING_SE:
update_resize_se (frame);
break;
default:
break;
}
frame->action = META_FRAME_ACTION_NONE;
}
break;
@ -339,18 +511,11 @@ meta_frame_event (MetaFrame *frame,
switch (frame->action)
{
case META_FRAME_ACTION_MOVING:
{
int x, y;
int new_x, new_y;
frame_query_root_pointer (frame, &x, &y);
update_move (frame);
break;
new_x = frame->rect.x + (x - frame->last_x);
new_y = frame->rect.y + (y - frame->last_y);
frame->last_x = x;
frame->last_y = y;
meta_frame_move (frame, new_x, new_y);
}
case META_FRAME_ACTION_RESIZING_SE:
update_resize_se (frame);
break;
default:
@ -388,11 +553,16 @@ meta_frame_event (MetaFrame *frame,
case CreateNotify:
break;
case DestroyNotify:
{
MetaDisplay *display;
meta_warning ("Unexpected destruction of frame 0x%lx, not sure if this should silently fail or be considered a bug\n", frame->xwindow);
meta_error_trap_push (frame->window->display);
display = frame->window->display;
meta_error_trap_push (display);
meta_window_destroy_frame (frame->window);
meta_error_trap_pop (frame->window->display);
meta_error_trap_pop (display);
return TRUE;
}
break;
case UnmapNotify:
frame->action = META_FRAME_ACTION_NONE;

View File

@ -28,7 +28,7 @@ typedef enum
{
META_FRAME_ACTION_NONE,
META_FRAME_ACTION_MOVING,
META_FRAME_ACTION_RESIZING
META_FRAME_ACTION_RESIZING_SE
} MetaFrameAction;
struct _MetaFrame
@ -43,6 +43,8 @@ struct _MetaFrame
* frame, not the result of ConfigureNotify
*/
MetaRectangle rect;
int child_x;
int child_y;
gpointer theme_data;
@ -50,16 +52,27 @@ struct _MetaFrame
/* reference point for drags */
int last_x, last_y;
int start_button;
guint theme_acquired : 1;
};
void meta_window_ensure_frame (MetaWindow *window);
void meta_window_destroy_frame (MetaWindow *window);
void meta_frame_move (MetaFrame *frame,
int root_x,
int root_y);
void meta_frame_child_configure_request (MetaFrame *frame);
void meta_frame_recalc_now (MetaFrame *frame);
void meta_frame_queue_recalc (MetaFrame *frame);
void meta_frame_queue_draw (MetaFrame *frame);
gboolean meta_frame_event (MetaFrame *frame,
XEvent *event);
#endif

View File

@ -1,6 +1,5 @@
#! /bin/bash
Xnest :1 -scrns 2 -geometry 270x270 &
sleep 1
DISPLAY=:1 unst $1 ./metacity
METACITY_UISLAVE_DIR=./uislave DISPLAY=:1 unst libtool --mode=execute gdb ./metacity
killall Xnest

View File

@ -24,11 +24,19 @@
#include "errors.h"
#include "window.h"
#include "colors.h"
#include "uislave.h"
#include <X11/cursorfont.h>
#include <locale.h>
#include <string.h>
static void ui_slave_func (MetaUISlave *uislave,
MetaMessage *message,
gpointer data);
static char* get_screen_name (MetaDisplay *display,
int number);
MetaScreen*
meta_screen_new (MetaDisplay *display,
int number)
@ -83,14 +91,19 @@ meta_screen_new (MetaDisplay *display,
screen->display = display;
screen->number = number;
screen->screen_name = get_screen_name (display, number);
screen->xscreen = ScreenOfDisplay (xdisplay, number);
screen->xroot = xroot;
screen->pango_context = NULL;
screen->engine = &meta_default_engine;
meta_verbose ("Added screen %d on display '%s' root 0x%lx\n",
screen->number, screen->display->name, screen->xroot);
screen->uislave = meta_ui_slave_new (screen->screen_name,
ui_slave_func,
screen);
meta_verbose ("Added screen %d ('%s') root 0x%lx\n",
screen->number, screen->screen_name, screen->xroot);
return screen;
}
@ -98,8 +111,10 @@ meta_screen_new (MetaDisplay *display,
void
meta_screen_free (MetaScreen *screen)
{
meta_ui_slave_free (screen->uislave);
if (screen->pango_context)
g_object_unref (G_OBJECT (screen->pango_context));
g_free (screen->screen_name);
g_free (screen);
}
@ -256,3 +271,64 @@ meta_screen_for_x_screen (Screen *xscreen)
return meta_display_screen_for_x_screen (display, xscreen);
}
static void
ui_slave_func (MetaUISlave *uislave,
MetaMessage *message,
gpointer data)
{
switch (message->header.message_code)
{
case MetaMessageCheckCode:
meta_verbose ("Received UI slave check message version: %s host alias: %s messages version: %d\n",
message->check.metacity_version,
message->check.host_alias,
message->check.messages_version);
if (strcmp (message->check.metacity_version, VERSION) != 0 ||
strcmp (message->check.host_alias, HOST_ALIAS) != 0 ||
message->check.messages_version != META_MESSAGES_VERSION)
{
meta_warning ("metacity-uislave has the wrong version; must use the one compiled with metacity\n");
meta_ui_slave_disable (uislave);
}
break;
default:
meta_verbose ("Received unhandled message from UI slave: %d\n",
message->header.message_code);
break;
}
}
static char*
get_screen_name (MetaDisplay *display,
int number)
{
char *p;
char *dname;
char *scr;
/* DisplayString gives us a sort of canonical display,
* vs. the user-entered name from XDisplayName()
*/
dname = g_strdup (DisplayString (display->xdisplay));
/* Change display name to specify this screen.
*/
p = strrchr (dname, ':');
if (p)
{
p = strchr (p, '.');
if (p)
*p = '\0';
}
scr = g_strdup_printf ("%s.%d", dname, number);
g_free (dname);
return scr;
}

View File

@ -29,6 +29,7 @@ struct _MetaScreen
{
MetaDisplay *display;
int number;
char *screen_name;
Screen *xscreen;
Window xroot;
@ -40,6 +41,8 @@ struct _MetaScreen
* root window)
*/
PangoContext *pango_context;
MetaUISlave *uislave;
};
MetaScreen* meta_screen_new (MetaDisplay *display,

View File

@ -20,10 +20,513 @@
*/
#include "uislave.h"
#include <unistd.h>
#include <string.h>
#include <stdlib.h>
#include <errno.h>
#include <signal.h>
typedef enum
{
READ_FAILED = 0, /* FALSE */
READ_OK,
READ_EOF
} ReadResult;
static void respawn_child (MetaUISlave *uislave);
static gboolean output_callback (GIOChannel *source,
GIOCondition condition,
gpointer data);
static gboolean error_callback (GIOChannel *source,
GIOCondition condition,
gpointer data);
static void kill_child (MetaUISlave *uislave);
static void reset_vals (MetaUISlave *uislave);
static ReadResult read_data (GString *str,
gint fd);
/* Message queue main loop source */
static gboolean mq_prepare (GSource *source,
gint *timeout);
static gboolean mq_check (GSource *source);
static gboolean mq_dispatch (GSource *source,
GSourceFunc callback,
gpointer user_data);
static void mq_destroy (GSource *source);
static GSourceFuncs mq_funcs = {
mq_prepare,
mq_check,
mq_dispatch,
mq_destroy
};
MetaUISlave*
meta_ui_slave_new (const char *display_name)
meta_ui_slave_new (const char *display_name,
MetaUISlaveFunc func,
gpointer data)
{
MetaUISlave *uislave;
GSource *source;
source = g_source_new (&mq_funcs, sizeof (MetaUISlave));
uislave = (MetaUISlave*) source;
uislave->display_name = g_strdup (display_name);
uislave->queue = g_queue_new ();
uislave->buf = g_string_new ("");
uislave->current_message = g_string_new ("");
reset_vals (uislave);
/* This may fail; all UISlave functions become no-ops
* if uislave->child_pids == 0, and metacity just runs
* with no UI features other than window borders.
*/
respawn_child (uislave);
g_source_set_priority (source, G_PRIORITY_DEFAULT);
g_source_set_can_recurse (source, TRUE);
g_source_set_callback (source, (GSourceFunc) func, data, NULL);
g_source_attach (source, NULL);
return uislave;
}
void
meta_ui_slave_free (MetaUISlave *uislave)
{
GSource *source;
source = (GSource*) uislave;
g_source_destroy (source);
}
void
meta_ui_slave_disable (MetaUISlave *uislave)
{
/* Change UI slave into "black hole" mode,
* we found out it's hosed for some reason.
*/
kill_child (uislave);
uislave->no_respawn = TRUE;
}
static void
respawn_child (MetaUISlave *uislave)
{
GError *error;
const char *uislavedir;
char *argv[] = { "./metacity-uislave", NULL };
char *envp[2] = { NULL, NULL };
int child_pid, inpipe, outpipe, errpipe;
if (uislave->no_respawn)
return;
uislavedir = g_getenv ("METACITY_UISLAVE_DIR");
if (uislavedir == NULL)
uislavedir = METACITY_LIBEXECDIR;
envp[0] = g_strconcat ("DISPLAY=", uislave->display_name, NULL);
error = NULL;
if (g_spawn_async_with_pipes (uislavedir,
argv,
envp,
/* flags */
0,
/* setup func, data */
NULL, NULL,
&child_pid,
&inpipe, &outpipe, &errpipe,
&error))
{
uislave->child_pid = child_pid;
uislave->in_pipe = inpipe;
uislave->err_pipe = errpipe;
uislave->out_poll.fd = outpipe;
uislave->out_poll.events = G_IO_IN;
uislave->err_channel = g_io_channel_unix_new (errpipe);
uislave->errwatch = g_io_add_watch (uislave->err_channel,
G_IO_IN,
error_callback,
uislave);
meta_verbose ("Spawned UI slave with PID %d\n", uislave->child_pid);
}
else
{
meta_warning ("Failed to create user interface process: %s\n",
error->message);
g_error_free (error);
}
g_free (envp[0]);
}
static void
append_pending (MetaUISlave *uislave)
{
int needed;
needed = uislave->current_required_len - uislave->current_message->len;
g_assert (needed >= 0);
needed = MIN (needed, uislave->buf->len);
/* Move data from buf to current_message */
g_string_append_len (uislave->current_message,
uislave->buf->str,
needed);
g_string_erase (uislave->buf,
0, needed);
}
static gboolean
output_callback (GIOChannel *source,
GIOCondition condition,
gpointer data)
{
/* Read messages from slave */
MetaUISlave *uislave;
ReadResult res;
uislave = data;
res = read_data (uislave->buf, uislave->out_pipe);
switch (res)
{
case READ_OK:
meta_verbose ("Read data from slave, %d bytes in buffer\n",
uislave->buf->len);
break;
case READ_EOF:
meta_verbose ("EOF reading stdout from slave process\n");
break;
case READ_FAILED:
/* read_data printed the error */
break;
}
return TRUE;
}
static gboolean
error_callback (GIOChannel *source,
GIOCondition condition,
gpointer data)
{
/* Relay slave errors to WM stderr */
#define BUFSIZE 1024
MetaUISlave *uislave;
char buf[1024];
int n;
uislave = data;
/* Classic loop from Stevens */
n = read (uislave->err_pipe, buf, BUFSIZE);
if (n > 0)
{
if (write (2, buf, n) != n)
; /* error, but printing a message to stderr will hardly help. */
}
else if (n < 0)
meta_warning (_("Error reading errors from UI slave: %s\n"),
g_strerror (errno));
return TRUE;
#undef BUFSIZE
}
static void
mq_queue_messages (MetaUISlave *uislave)
{
if (uislave->buf->len == 0)
return;
if (uislave->current_message->len > 0)
{
/* We had a pending message. */
append_pending (uislave);
}
else if (uislave->buf->len > META_MESSAGE_ESCAPE_LEN)
{
/* See if we can start a current message */
const char *p;
int esc_pos;
const char *esc;
MetaMessageHeader header;
/* note that the string from the UI slave includes the nul byte */
esc = META_MESSAGE_ESCAPE;
esc_pos = 0;
p = uislave->buf->str;
while (p != (uislave->buf->str + uislave->buf->len) &&
esc_pos < META_MESSAGE_ESCAPE_LEN)
{
if (*p != esc[esc_pos])
esc_pos = 0;
else
++esc_pos;
++p;
}
if (esc_pos == META_MESSAGE_ESCAPE_LEN)
{
/* We found an entire escape sequence; can safely toss
* out the entire buffer before it
*/
int ignored;
ignored = p - uislave->buf->str;
ignored -= META_MESSAGE_ESCAPE_LEN;
g_assert (ignored >= 0);
if (ignored > 0)
{
meta_verbose ("Ignoring %d bytes from UI slave\n",
ignored);
g_string_erase (uislave->buf, 0, ignored);
}
}
else if (esc_pos == 0)
{
/* End of buffer doesn't begin an escape sequence;
* toss out entire buffer.
*/
meta_verbose ("Ignoring %d bytes from UI slave\n",
uislave->buf->len);
g_string_truncate (uislave->buf, 0);
}
if (uislave->buf->len < (META_MESSAGE_ESCAPE_LEN + sizeof (MetaMessageHeader)))
return; /* Not enough data yet. */
memcpy (&header, uislave->buf->str + META_MESSAGE_ESCAPE_LEN, sizeof (MetaMessageHeader));
/* Length includes the header even though it's in the header. */
meta_verbose ("Read header code: %d length: %d from UI slave\n",
header.message_code, header.length);
uislave->current_required_len = header.length;
g_string_erase (uislave->buf, 0, META_MESSAGE_ESCAPE_LEN);
append_pending (uislave);
}
g_assert (uislave->current_message->len <= uislave->current_required_len);
if (uislave->current_required_len > 0 &&
uislave->current_message->len == uislave->current_required_len)
{
MetaMessage *message;
message = g_new (MetaMessage, 1);
memcpy (message,
uislave->current_message->str, uislave->current_message->len);
g_queue_push_tail (uislave->queue, message);
meta_verbose ("Added %d-byte message to queue\n",
uislave->current_message->len);
uislave->current_required_len = 0;
g_string_truncate (uislave->current_message, 0);
}
}
static gboolean
mq_messages_pending (MetaUISlave *uislave)
{
return uislave->queue->length > 0 || uislave->buf->len > 0;
}
static gboolean
mq_prepare (GSource *source, gint *timeout)
{
MetaUISlave *uislave;
uislave = (MetaUISlave*) source;
*timeout = -1;
return mq_messages_pending (uislave);
}
static gboolean
mq_check (GSource *source)
{
MetaUISlave *uislave;
uislave = (MetaUISlave*) source;
return mq_messages_pending (uislave);
}
static gboolean
mq_dispatch (GSource *source, GSourceFunc callback, gpointer user_data)
{
MetaUISlave *uislave;
uislave = (MetaUISlave*) source;
mq_queue_messages (uislave);
if (uislave->queue->length > 0)
{
MetaUISlaveFunc func;
MetaMessage *msg;
static int count = 0;
++count;
msg = g_queue_pop_head (uislave->queue);
func = (MetaUISlaveFunc) callback;
(* func) (uislave, msg, user_data);
meta_verbose ("%d messages dispatched\n", count);
g_free (msg);
}
return TRUE;
}
static void
kill_child (MetaUISlave *uislave)
{
if (uislave->outwatch != 0)
g_source_remove (uislave->outwatch);
if (uislave->errwatch != 0)
g_source_remove (uislave->errwatch);
if (uislave->out_channel)
g_io_channel_unref (uislave->out_channel);
if (uislave->err_channel)
g_io_channel_unref (uislave->err_channel);
if (uislave->out_pipe >= 0)
close (uislave->out_pipe);
if (uislave->in_pipe >= 0)
close (uislave->in_pipe);
if (uislave->err_pipe >= 0)
close (uislave->err_pipe);
while (uislave->queue->length > 0)
{
MetaMessage *msg;
msg = g_queue_pop_head (uislave->queue);
g_free (msg);
}
if (uislave->buf->len > 0)
g_string_truncate (uislave->buf, 0);
if (uislave->current_message->len > 0)
g_string_truncate (uislave->current_message, 0);
if (uislave->child_pid > 0)
{
/* don't care if this fails except in verbose mode */
if (kill (uislave->child_pid, SIGTERM) != 0)
{
meta_verbose ("Kill of UI slave process %d failed: %s\n",
uislave->child_pid, g_strerror (errno));
}
uislave->child_pid = 0;
}
reset_vals (uislave);
}
static void
reset_vals (MetaUISlave *uislave)
{
uislave->child_pid = 0;
uislave->in_pipe = -1;
uislave->out_pipe = -1;
uislave->err_pipe = -1;
uislave->no_respawn = FALSE;
uislave->out_channel = NULL;
uislave->err_channel = NULL;
uislave->outwatch = 0;
uislave->errwatch = 0;
uislave->current_required_len = 0;
}
static void
mq_destroy (GSource *source)
{
MetaUISlave *uislave;
uislave = (MetaUISlave*) source;
meta_verbose ("Deleting UI slave for display '%s'\n",
uislave->display_name);
kill_child (uislave);
g_string_free (uislave->buf, TRUE);
g_string_free (uislave->current_message, TRUE);
g_queue_free (uislave->queue);
g_free (uislave->display_name);
/* source itself is freed by glib */
}
static ReadResult
read_data (GString *str,
gint fd)
{
#define BUFSIZE 16
gint bytes;
gchar buf[BUFSIZE];
again:
bytes = read (fd, &buf, BUFSIZE);
if (bytes == 0)
return READ_EOF;
else if (bytes > 0)
{
g_string_append_len (str, buf, bytes);
return READ_OK;
}
else if (bytes < 0 && errno == EINTR)
goto again;
else if (bytes < 0)
{
meta_warning (_("Failed to read data from UI slave: %s\n"),
g_strerror (errno));
return READ_FAILED;
}
else
return READ_OK;
}

View File

@ -23,17 +23,39 @@
#define META_UI_SLAVE_H
#include "util.h"
#include <X11/Xlib.h>
#include <X11/Xutil.h>
#include "uislave/messages.h"
#include "display.h"
typedef struct _MetaUISlave MetaUISlave;
typedef void (* MetaUISlaveFunc) (MetaUISlave *uislave,
MetaMessage *message,
gpointer data);
struct _MetaUISlave
{
GSource source;
char *display_name;
int child_pid;
int in_pipe;
int err_pipe;
GPollFD out_poll;
GIOChannel *err_channel;
unsigned int errwatch;
GQueue *queue;
GString *buf;
GString *current_message;
int current_required_len;
/* if we determine that our available slave is hosed,
* set this bit.
*/
guint no_respawn : 1;
};
MetaUISlave* meta_ui_slave_new (const char *display_name);
MetaUISlave* meta_ui_slave_new (const char *display_name,
MetaUISlaveFunc func,
gpointer data);
void meta_ui_slave_free (MetaUISlave *uislave);
void meta_ui_slave_disable (MetaUISlave *uislave);
#endif

View File

@ -1,8 +1,10 @@
INCLUDES=@UISLAVE_CFLAGS@
INCLUDES=@UISLAVE_CFLAGS@ -DHOST_ALIAS=\"@HOST_ALIAS@\"
metacity_uislave_SOURCES = \
main.c
main.c \
messages.c \
messages.h
libexec_PROGRAMS=metacity-uislave

View File

@ -19,9 +19,75 @@
* 02111-1307, USA.
*/
#include "messages.h"
#include <stdio.h>
#include <string.h>
#include <gtk/gtk.h>
void
meta_ui_warning (const char *format, ...)
{
va_list args;
gchar *str;
g_return_if_fail (format != NULL);
va_start (args, format);
str = g_strdup_vprintf (format, args);
va_end (args);
fputs (str, stderr);
g_free (str);
}
int
main (int argc, char **argv)
{
int i;
/* report our nature to the window manager */
meta_message_send_check ();
#if 1
/* Try breaking message queue system. */
i = 0;
while (i < 100)
{
meta_message_send_check ();
if (g_random_boolean ())
{
int j;
if (g_random_boolean ())
j = g_random_int_range (0, 15);
else
j = g_random_int_range (0, 1000);
while (j > 0)
{
char b;
b = g_random_int_range (0, 256);
write (1, &b, 1);
--j;
}
}
++i;
}
#endif
gtk_init (&argc, &argv);
gtk_main ();
return 0;
}

123
src/uislave/messages.c Normal file
View File

@ -0,0 +1,123 @@
/* Metacity UI Slave Messages */
/*
* Copyright (C) 2001 Havoc Pennington
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License as
* published by the Free Software Foundation; either version 2 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
* 02111-1307, USA.
*/
#include "messages.h"
#include "main.h"
#include <errno.h>
#include <stdlib.h>
#include <unistd.h>
#include <stdio.h>
#include <string.h>
#include <config.h>
typedef enum
{
READ_FAILED = 0, /* FALSE */
READ_OK,
READ_EOF
} ReadResult;
static ReadResult read_data (GString *str,
gint fd);
static void send_message (MetaMessage *message);
void
meta_message_send_check (void)
{
MetaMessageCheck check;
memset (&check, 0, sizeof (check));
check.header.message_code = MetaMessageCheckCode;
check.header.length = sizeof (check);
strcpy (check.metacity_version, VERSION);
strcpy (check.host_alias, HOST_ALIAS);
check.metacity_version[META_MESSAGE_MAX_VERSION_LEN] = '\0';
check.host_alias[META_MESSAGE_MAX_HOST_ALIAS_LEN] = '\0';
check.messages_version = META_MESSAGES_VERSION;
send_message ((MetaMessage*)&check);
}
static int
write_bytes (void *buf, int bytes)
{
const char *p;
p = (char*) buf;
while (bytes > 0)
{
int written;
written = write (1, p, bytes);
if (written < 0)
return -1;
bytes -= written;
p += written;
}
return 0;
}
static void
send_message (MetaMessage *message)
{
/* Not much point checking for errors here. We can't
* really report them anyway.
*/
write_bytes (META_MESSAGE_ESCAPE, META_MESSAGE_ESCAPE_LEN);
write_bytes (message, message->header.length);
}
static ReadResult
read_data (GString *str,
gint fd)
{
gint bytes;
gchar buf[4096];
again:
bytes = read (fd, &buf, 4096);
if (bytes == 0)
return READ_EOF;
else if (bytes > 0)
{
g_string_append_len (str, buf, bytes);
return READ_OK;
}
else if (bytes < 0 && errno == EINTR)
goto again;
else if (bytes < 0)
{
meta_ui_warning (_("Failed to read data from window manager (%s)\n"),
g_strerror (errno));
return READ_FAILED;
}
else
return READ_OK;
}

115
src/uislave/messages.h Normal file
View File

@ -0,0 +1,115 @@
/* Metacity UI Slave Messages */
/*
* Copyright (C) 2001 Havoc Pennington
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License as
* published by the Free Software Foundation; either version 2 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
* 02111-1307, USA.
*/
#ifndef META_UI_SLAVE_MESSAGES_H
#define META_UI_SLAVE_MESSAGES_H
#include <glib.h>
/* This header is shared between the WM and the UI slave */
/* Note that our IPC can be kind of lame; we trust both sides
* of the connection, and assume that they were compiled at the
* same time vs. the same libs on the same arch
*/
/* We increment this when we change this header, so we can
* check for mismatched UI slave and WM
*/
#define META_MESSAGES_VERSION 1
/* We have an escape sequence, just in case some part of GTK
* decides to write to stdout, so that we have a good chance
* of surviving that. GTK probably won't print this string.
* This string has to stay the same always so we can ping
* old UI slaves.
*/
#define META_MESSAGE_ESCAPE "|~-metacity-~|"
/* len includes nul byte which is a required part of the escape */
#define META_MESSAGE_ESCAPE_LEN 15
#define META_MESSAGE_MAX_VERSION_LEN 15
#define META_MESSAGE_MAX_HOST_ALIAS_LEN 50
typedef union _MetaMessage MetaMessage;
typedef struct _MetaMessageHeader MetaMessageHeader;
typedef struct _MetaMessageCheck MetaMessageCheck;
typedef struct _MetaMessageShowTip MetaMessageShowTip;
typedef struct _MetaMessageHideTip MetaMessageHideTip;
typedef enum
{
/* Keep NullCode and CheckCode unchanged, as with the escape sequence,
* so we can check old UI slaves.
*/
MetaMessageNullCode,
MetaMessageCheckCode,
MetaMessageShowTipCode,
MetaMessageHideTipCode
} MetaMessageCode;
struct _MetaMessageHeader
{
MetaMessageCode message_code;
int length;
};
/* just a ping to see if we have the right
* version of UI slave.
*/
struct _MetaMessageCheck
{
MetaMessageHeader header;
/* it's OK if the max sizes aren't large enough in all cases, these
* are just paranoia checks
*/
char metacity_version[META_MESSAGE_MAX_VERSION_LEN + 1];
char host_alias[META_MESSAGE_MAX_HOST_ALIAS_LEN + 1];
int messages_version;
};
struct _MetaMessageShowTip
{
MetaMessageHeader header;
int root_x;
int root_y;
/* Then a nul-terminated string follows */
};
struct _MetaMessageHideTip
{
MetaMessageHeader header;
/* just hides the current tip */
};
union _MetaMessage
{
MetaMessageHeader header;
MetaMessageCheck check;
MetaMessageShowTip show_tip;
MetaMessageShowTip hide_tip;
};
/* Slave-side message send/read code */
void meta_message_send_check (void);
#endif

View File

@ -54,6 +54,7 @@ void meta_fatal (const char *format,
...) G_GNUC_PRINTF (1, 2);
/* FIXME */
#include <config.h>
#define _(x) x
#endif

View File

@ -34,13 +34,11 @@ static int update_size_hints (MetaWindow *window);
static int update_title (MetaWindow *window);
static int update_protocols (MetaWindow *window);
static gboolean process_configure_request (MetaWindow *window,
XConfigureRequestEvent *event);
int x, int y, int width, int height,
int border_width);
static gboolean process_property_notify (MetaWindow *window,
XPropertyEvent *event);
MetaWindow*
meta_window_new (MetaDisplay *display, Window xwindow)
{
@ -57,9 +55,7 @@ meta_window_new (MetaDisplay *display, Window xwindow)
xwindow, &attrs) == Success &&
attrs.override_redirect)
{
/* Oops. Probably attempted to manage override redirect window
* in initial screen_manage_all_windows() call.
*/
meta_verbose ("Deciding not to manage override_redirect window 0x%lx\n", xwindow);
meta_error_trap_pop (display);
return NULL;
}
@ -67,7 +63,7 @@ meta_window_new (MetaDisplay *display, Window xwindow)
XAddToSaveSet (display->xdisplay, xwindow);
XSelectInput (display->xdisplay, xwindow,
StructureNotifyMask);
PropertyChangeMask);
if (meta_error_trap_pop (display) != Success)
{
@ -105,6 +101,12 @@ meta_window_new (MetaDisplay *display, Window xwindow)
window->rect.y = attrs.y;
window->rect.width = attrs.width;
window->rect.height = attrs.height;
window->size_hints.x = attrs.x;
window->size_hints.y = attrs.y;
window->size_hints.width = attrs.width;
window->size_hints.height = attrs.height;
window->depth = attrs.depth;
window->xvisual = attrs.visual;
@ -113,13 +115,16 @@ meta_window_new (MetaDisplay *display, Window xwindow)
window->desc = g_strdup_printf ("0x%lx", window->xwindow);
window->frame = NULL;
meta_display_register_x_window (display, &window->xwindow, window);
update_size_hints (window);
update_title (window);
update_protocols (window);
window->frame = NULL;
meta_window_resize (window, window->size_hints.width, window->size_hints.height);
meta_window_ensure_frame (window);
return window;
@ -132,10 +137,10 @@ meta_window_free (MetaWindow *window)
meta_display_unregister_x_window (window->display, window->xwindow);
g_free (window->title);
meta_window_destroy_frame (window);
g_free (window->title);
g_free (window->desc);
g_free (window);
}
@ -159,13 +164,56 @@ meta_window_hide (MetaWindow *window)
window->iconic = TRUE;
}
void
meta_window_resize (MetaWindow *window,
int w,
int h)
{
meta_verbose ("Resizing %s to %d x %d\n", window->desc, w, h);
constrain_size (window, w, h, &w, &h);
meta_verbose ("Constrained resize of %s to %d x %d\n", window->desc, w, h);
if (w != window->rect.width ||
h != window->rect.height)
{
meta_error_trap_push (window->display);
XResizeWindow (window->display->xdisplay,
window->xwindow,
w, h);
meta_error_trap_pop (window->display);
window->rect.width = w;
window->rect.height = h;
if (window->frame)
meta_frame_queue_recalc (window->frame);
}
}
gboolean
meta_window_configure_request (MetaWindow *window,
XEvent *event)
{
return process_configure_request (window,
event->xconfigurerequest.x,
event->xconfigurerequest.y,
event->xconfigurerequest.width,
event->xconfigurerequest.height,
event->xconfigurerequest.border_width);
}
gboolean
meta_window_property_notify (MetaWindow *window,
XEvent *event)
{
return process_property_notify (window, &event->xproperty);
}
gboolean
meta_window_event (MetaWindow *window,
XEvent *event)
{
if (window->frame &&
event->xany.window == window->frame->xwindow)
return meta_frame_event (window->frame, event);
if (event->xany.window != window->xwindow)
return FALSE;
@ -203,7 +251,7 @@ meta_window_event (MetaWindow *window,
case CreateNotify:
break;
case DestroyNotify:
meta_window_free (window);
return TRUE;
break;
case UnmapNotify:
@ -218,17 +266,10 @@ meta_window_event (MetaWindow *window,
case ReparentNotify:
break;
case ConfigureNotify:
if (event->xconfigure.override_redirect)
{
/* Unmanage it, override_redirect was toggled on?
* Can this happen?
*/
meta_window_free (window);
return TRUE;
}
break;
case ConfigureRequest:
return process_configure_request (window, &event->xconfigurerequest);
break;
case GravityNotify:
break;
@ -239,7 +280,7 @@ meta_window_event (MetaWindow *window,
case CirculateRequest:
break;
case PropertyNotify:
return process_property_notify (window, &event->xproperty);
break;
case SelectionClear:
break;
@ -269,24 +310,75 @@ process_property_notify (MetaWindow *window,
event->atom == window->display->atom_net_wm_name)
{
update_title (window);
if (window->frame)
meta_frame_queue_recalc (window->frame);
}
else if (event->atom == XA_WM_NORMAL_HINTS)
{
update_size_hints (window);
/* See if we need to constrain current size */
meta_window_resize (window, window->rect.width, window->rect.height);
}
else if (event->atom == XA_WM_PROTOCOLS)
else if (event->atom == window->display->atom_wm_protocols)
{
update_protocols (window);
if (window->frame)
meta_frame_queue_recalc (window->frame);
}
return TRUE;
}
static void
send_configure_notify (MetaWindow *window)
{
XEvent event;
/* from twm */
event.type = ConfigureNotify;
event.xconfigure.display = window->display->xdisplay;
event.xconfigure.event = window->xwindow;
event.xconfigure.window = window->xwindow;
event.xconfigure.x = window->rect.x - window->border_width;
event.xconfigure.y = window->rect.y - window->border_width;
if (window->frame)
{
/* Need to be in root window coordinates */
event.xconfigure.x += window->frame->rect.x;
event.xconfigure.y += window->frame->rect.y;
}
event.xconfigure.width = window->rect.width;
event.xconfigure.height = window->rect.height;
event.xconfigure.border_width = window->border_width; /* requested not actual */
event.xconfigure.above = None; /* FIXME */
event.xconfigure.override_redirect = False;
meta_verbose ("Sending synthetic configure notify to %s with x: %d y: %d w: %d h: %d\n",
window->desc,
event.xconfigure.x, event.xconfigure.y,
event.xconfigure.width, event.xconfigure.height);
meta_error_trap_push (window->display);
XSendEvent(window->display->xdisplay,
window->xwindow,
False, StructureNotifyMask, &event);
meta_error_trap_pop (window->display);
}
static gboolean
process_configure_request (MetaWindow *window,
XConfigureRequestEvent *event)
int x, int y,
int width, int height,
int border_width)
{
/* ICCCM 4.1.5 */
XWindowChanges values;
unsigned int mask;
int client_x, client_y;
/* Note that x, y is the corner of the window border,
* and width, height is the size of the window inside
@ -294,13 +386,83 @@ process_configure_request (MetaWindow *window,
* because we don't believe in clients who use lame-ass
* X features like that.
*/
window->border_width = event->border_width;
window->size_hints.x = event->x;
window->size_hints.y = event->y;
window->size_hints.width = event->width;
window->size_hints.height = event->height;
window->border_width = border_width;
/* FIXME */
/* We're ignoring the value_mask here, since sizes
* not in the mask will be the current window geometry.
*/
window->size_hints.x = x;
window->size_hints.y = y;
window->size_hints.width = width;
window->size_hints.height = height;
constrain_size (window,
window->size_hints.width,
window->size_hints.height,
&window->size_hints.width,
&window->size_hints.height);
meta_verbose ("Constrained configure request size to %d x %d\n",
window->size_hints.width, window->size_hints.height);
if (window->frame)
{
meta_frame_child_configure_request (window->frame);
client_x = window->frame->child_x;
client_y = window->frame->child_y;
meta_verbose ("Will place client window %s inside frame at %d,%d\n",
window->desc, client_x, client_y);
}
else
{
client_x = window->size_hints.x;
client_y = window->size_hints.y;
meta_verbose ("Will place client window %s at root coordinate %d,%d\n",
window->desc, client_x, client_y);
}
values.border_width = 0;
values.x = client_x;
values.y = client_y;
values.width = window->size_hints.width;
values.height = window->size_hints.height;
mask = 0;
if (window->border_width != 0)
mask |= CWBorderWidth;
if (values.x != window->rect.x)
mask |= CWX;
if (values.y != window->rect.y)
mask |= CWY;
if (values.width != window->rect.width)
mask |= CWWidth;
if (values.height != window->rect.height)
mask |= CWHeight;
window->rect.x = values.x;
window->rect.y = values.y;
window->rect.width = values.width;
window->rect.height = values.height;
meta_error_trap_push (window->display);
XConfigureWindow (window->display->xdisplay,
window->xwindow,
mask,
&values);
meta_error_trap_pop (window->display);
if (mask & (CWBorderWidth | CWWidth | CWHeight))
{
/* Resizing, no synthetic ConfigureNotify, third case in 4.1.5 */
}
else
{
/* Moving but not resizing, second case in 4.1.5, or
* have to send the ConfigureNotify, first case in 4.1.5
*/
send_configure_notify (window);
}
return TRUE;
}
@ -333,7 +495,12 @@ update_size_hints (MetaWindow *window)
window->size_hints.height = h;
if (window->size_hints.flags & PBaseSize)
;
{
meta_verbose ("Window %s sets base size %d x %d\n",
window->desc,
window->size_hints.base_width,
window->size_hints.base_height);
}
else if (window->size_hints.flags & PMinSize)
{
window->size_hints.base_width = window->size_hints.min_width;
@ -347,7 +514,12 @@ update_size_hints (MetaWindow *window)
window->size_hints.flags |= PBaseSize;
if (window->size_hints.flags & PMinSize)
;
{
meta_verbose ("Window %s sets min size %d x %d\n",
window->desc,
window->size_hints.min_width,
window->size_hints.min_height);
}
else if (window->size_hints.flags & PBaseSize)
{
window->size_hints.min_width = window->size_hints.base_width;
@ -361,7 +533,12 @@ update_size_hints (MetaWindow *window)
window->size_hints.flags |= PMinSize;
if (window->size_hints.flags & PMaxSize)
;
{
meta_verbose ("Window %s sets max size %d x %d\n",
window->desc,
window->size_hints.max_width,
window->size_hints.max_height);
}
else
{
window->size_hints.max_width = G_MAXINT;
@ -370,7 +547,22 @@ update_size_hints (MetaWindow *window)
}
if (window->size_hints.flags & PResizeInc)
;
{
meta_verbose ("Window %s sets resize width inc: %d height inc: %d\n",
window->desc,
window->size_hints.width_inc,
window->size_hints.height_inc);
if (window->size_hints.width_inc == 0)
{
window->size_hints.width_inc = 1;
meta_verbose ("Corrected 0 width_inc to 1\n");
}
if (window->size_hints.height_inc == 0)
{
window->size_hints.height_inc = 1;
meta_verbose ("Corrected 0 height_inc to 1\n");
}
}
else
{
window->size_hints.width_inc = 1;
@ -380,6 +572,13 @@ update_size_hints (MetaWindow *window)
if (window->size_hints.flags & PAspect)
{
meta_verbose ("Window %s sets min_aspect: %d/%d max_aspect: %d/%d\n",
window->desc,
window->size_hints.min_aspect.x,
window->size_hints.min_aspect.y,
window->size_hints.max_aspect.x,
window->size_hints.max_aspect.y);
/* don't divide by 0 */
if (window->size_hints.min_aspect.y < 1)
window->size_hints.min_aspect.y = 1;
@ -396,15 +595,17 @@ update_size_hints (MetaWindow *window)
}
if (window->size_hints.flags & PWinGravity)
;
{
meta_verbose ("Window %s sets gravity %d\n",
window->desc,
window->size_hints.win_gravity);
}
else
{
window->size_hints.win_gravity = NorthWestGravity;
window->size_hints.flags |= PWinGravity;
}
/* FIXME constrain the window to these hints */
return meta_error_trap_pop (window->display);
}
@ -487,30 +688,36 @@ update_title (MetaWindow *window)
static int
update_protocols (MetaWindow *window)
{
Atom *protocols;
int n_protocols;
Atom *protocols = NULL;
int n_protocols = 0;
int i;
meta_error_trap_push (window->display);
XGetWMProtocols (window->display->xdisplay,
window->xwindow,
&protocols,
&n_protocols);
window->take_focus = FALSE;
window->delete_window = FALSE;
meta_error_trap_push (window->display);
if (XGetWMProtocols (window->display->xdisplay,
window->xwindow,
&protocols,
&n_protocols) == Success)
{
i = 0;
while (i < n_protocols)
{
if (protocols[i] == _XA_WM_TAKE_FOCUS)
window->takes_focus = TRUE;
else if (protocols[i] == _XA_WM_DELETE_WINDOW)
if (protocols[i] == window->display->atom_wm_take_focus)
window->take_focus = TRUE;
else if (protocols[i] == window->display->atom_wm_delete_window)
window->delete_window = TRUE;
++i;
}
if (protocols)
XFree (protocols);
}
meta_verbose ("Window %s has take_focus = %d delete_window = %d\n",
window->desc, window->take_focus, window->delete_window);
return meta_error_trap_pop (window->display);
}
@ -591,3 +798,4 @@ constrain_size (MetaWindow *window,
*new_width = width;
*new_height = height;
}

View File

@ -56,10 +56,17 @@ MetaWindow* meta_window_new (MetaDisplay *display,
void meta_window_free (MetaWindow *window);
void meta_window_show (MetaWindow *window);
void meta_window_hide (MetaWindow *window);
void meta_window_resize (MetaWindow *window,
int w,
int h);
gboolean meta_window_event (MetaWindow *window,
gboolean meta_window_configure_request (MetaWindow *window,
XEvent *event);
gboolean meta_window_property_notify (MetaWindow *window,
XEvent *event);
#endif