mutter/src/display.c

804 lines
20 KiB
C
Raw Normal View History

2001-05-30 11:36:31 -04:00
/* Metacity X display handler */
/*
* 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 "display.h"
#include "util.h"
#include "main.h"
#include "screen.h"
#include "window.h"
2001-06-02 21:33:27 -04:00
#include "frame.h"
2001-06-04 00:58:22 -04:00
#include "errors.h"
2001-06-06 00:47:37 -04:00
#include "keybindings.h"
#include "workspace.h"
2001-05-30 11:36:31 -04:00
static GSList *all_displays = NULL;
2001-06-02 21:33:27 -04:00
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);
2001-05-30 11:36:31 -04:00
static gint
unsigned_long_equal (gconstpointer v1,
gconstpointer v2)
{
return *((const gulong*) v1) == *((const gulong*) v2);
}
static guint
unsigned_long_hash (gconstpointer v)
{
gulong val = * (const gulong *) v;
/* I'm not sure this works so well. */
#if G_SIZEOF_LONG > 4
return (guint) (val ^ (val >> 32));
#else
return val;
#endif
}
gboolean
meta_display_open (const char *name)
{
MetaDisplay *display;
Display *xdisplay;
2001-05-30 23:30:58 -04:00
GSList *screens;
2001-05-31 02:42:58 -04:00
GSList *tmp;
2001-05-30 11:36:31 -04:00
int i;
2001-06-04 02:17:52 -04:00
/* Remember to edit code that assigns each atom to display struct
* when adding an atom name here.
*/
char *atom_names[] = {
"_NET_WM_NAME",
"WM_PROTOCOLS",
"WM_TAKE_FOCUS",
2001-06-06 00:47:37 -04:00
"WM_DELETE_WINDOW",
"WM_STATE"
2001-06-04 02:17:52 -04:00
};
2001-06-02 00:14:18 -04:00
Atom atoms[G_N_ELEMENTS(atom_names)];
2001-05-30 11:36:31 -04:00
meta_verbose ("Opening display '%s'\n", XDisplayName (name));
xdisplay = XOpenDisplay (name);
if (xdisplay == NULL)
{
meta_warning (_("Failed to open X Window System display '%s'\n"),
XDisplayName (name));
return FALSE;
}
2001-05-31 02:42:58 -04:00
if (meta_is_syncing ())
XSynchronize (xdisplay, True);
2001-05-30 23:30:58 -04:00
display = g_new (MetaDisplay, 1);
2001-06-02 21:33:27 -04:00
/* here we use XDisplayName which is what the user
* probably put in, vs. DisplayString(display) which is
* canonicalized by XOpenDisplay()
*/
2001-05-30 11:36:31 -04:00
display->name = g_strdup (XDisplayName (name));
display->xdisplay = xdisplay;
2001-05-30 23:30:58 -04:00
display->error_traps = NULL;
2001-05-30 11:36:31 -04:00
2001-06-06 00:47:37 -04:00
display->workspaces = NULL;
2001-05-30 23:30:58 -04:00
/* we have to go ahead and do this so error handlers work */
2001-05-30 11:36:31 -04:00
all_displays = g_slist_prepend (all_displays, display);
2001-06-06 00:47:37 -04:00
meta_display_init_keys (display);
2001-05-30 11:36:31 -04:00
2001-05-30 23:30:58 -04:00
screens = NULL;
2001-05-30 11:36:31 -04:00
i = 0;
while (i < ScreenCount (xdisplay))
{
2001-05-30 23:30:58 -04:00
MetaScreen *screen;
screen = meta_screen_new (display, i);
if (screen)
screens = g_slist_prepend (screens, screen);
2001-05-30 11:36:31 -04:00
++i;
}
2001-05-30 23:30:58 -04:00
if (screens == NULL)
{
/* This would typically happen because all the screens already
* have window managers
*/
XCloseDisplay (xdisplay);
all_displays = g_slist_remove (all_displays, display);
g_free (display->name);
g_free (display);
return FALSE;
}
display->screens = screens;
display->events = meta_event_queue_new (display->xdisplay,
event_queue_callback,
display);
display->window_ids = g_hash_table_new (unsigned_long_hash, unsigned_long_equal);
2001-05-31 02:42:58 -04:00
display->server_grab_count = 0;
2001-06-02 00:14:18 -04:00
XInternAtoms (display->xdisplay, atom_names, G_N_ELEMENTS (atom_names),
False, atoms);
display->atom_net_wm_name = atoms[0];
2001-06-04 02:17:52 -04:00
display->atom_wm_protocols = atoms[1];
display->atom_wm_take_focus = atoms[2];
display->atom_wm_delete_window = atoms[3];
2001-06-06 00:47:37 -04:00
display->atom_wm_state = atoms[4];
2001-06-02 00:14:18 -04:00
2001-05-31 02:42:58 -04:00
/* Now manage all existing windows */
tmp = display->screens;
while (tmp != NULL)
{
meta_screen_manage_all_windows (tmp->data);
tmp = tmp->next;
}
2001-05-30 11:36:31 -04:00
return TRUE;
}
static void
2001-06-03 14:33:59 -04:00
listify_func (gpointer key, gpointer value, gpointer data)
2001-05-30 11:36:31 -04:00
{
2001-06-03 14:33:59 -04:00
GSList **listp;
2001-05-30 11:36:31 -04:00
2001-06-03 14:33:59 -04:00
listp = data;
*listp = g_slist_prepend (*listp, value);
}
2001-05-30 11:36:31 -04:00
2001-06-03 14:33:59 -04:00
static gint
ptrcmp (gconstpointer a, gconstpointer b)
{
if (a < b)
return -1;
else if (a > b)
return 1;
else
return 0;
2001-05-30 11:36:31 -04:00
}
void
meta_display_close (MetaDisplay *display)
{
2001-06-03 14:33:59 -04:00
GSList *winlist;
GSList *tmp;
2001-05-30 23:30:58 -04:00
if (display->error_traps)
meta_bug ("Display closed with error traps pending\n");
2001-06-03 14:33:59 -04:00
winlist = NULL;
2001-05-30 11:36:31 -04:00
g_hash_table_foreach (display->window_ids,
2001-06-03 14:33:59 -04:00
listify_func,
&winlist);
winlist = g_slist_sort (winlist, ptrcmp);
2001-06-04 02:17:52 -04:00
/* Unmanage all windows */
meta_display_grab (display);
2001-06-03 14:33:59 -04:00
tmp = winlist;
while (tmp != NULL)
2001-06-04 00:58:22 -04:00
{
2001-06-03 14:33:59 -04:00
if (tmp->next == NULL ||
(tmp->next && tmp->next->data != tmp->data))
meta_window_free (tmp->data);
2001-06-04 00:58:22 -04:00
tmp = tmp->next;
2001-06-03 14:33:59 -04:00
}
g_slist_free (winlist);
2001-06-04 02:17:52 -04:00
meta_display_ungrab (display);
2001-06-04 00:58:22 -04:00
/* Must be after all calls to meta_window_free() since they
* unregister windows
*/
g_hash_table_destroy (display->window_ids);
2001-05-30 11:36:31 -04:00
meta_event_queue_free (display->events);
XCloseDisplay (display->xdisplay);
g_free (display->name);
all_displays = g_slist_remove (all_displays, display);
g_free (display);
if (all_displays == NULL)
{
2001-05-30 23:30:58 -04:00
meta_verbose ("Last display closed, exiting\n");
2001-05-30 11:36:31 -04:00
meta_quit (META_EXIT_SUCCESS);
}
}
MetaScreen*
meta_display_screen_for_root (MetaDisplay *display,
Window xroot)
{
GSList *tmp;
tmp = display->screens;
while (tmp != NULL)
{
MetaScreen *screen = tmp->data;
if (xroot == screen->xroot)
return screen;
tmp = tmp->next;
}
return NULL;
}
2001-05-31 23:00:01 -04:00
MetaScreen*
meta_display_screen_for_x_screen (MetaDisplay *display,
Screen *xscreen)
{
GSList *tmp;
tmp = display->screens;
while (tmp != NULL)
{
MetaScreen *screen = tmp->data;
if (xscreen == screen->xscreen)
return screen;
tmp = tmp->next;
}
return NULL;
}
2001-05-31 02:42:58 -04:00
/* Grab/ungrab routines taken from fvwm */
void
meta_display_grab (MetaDisplay *display)
{
if (display->server_grab_count == 0)
{
XSync (display->xdisplay, False);
XGrabServer (display->xdisplay);
}
XSync (display->xdisplay, False);
display->server_grab_count += 1;
}
void
meta_display_ungrab (MetaDisplay *display)
{
if (display->server_grab_count == 0)
meta_bug ("Ungrabbed non-grabbed server\n");
2001-06-06 00:47:37 -04:00
2001-05-31 02:42:58 -04:00
display->server_grab_count -= 1;
if (display->server_grab_count == 0)
{
2001-06-06 00:47:37 -04:00
/* FIXME we want to purge all pending "queued" stuff
* at this point, such as window hide/show
*/
2001-05-31 02:42:58 -04:00
XUngrabServer (display->xdisplay);
}
XSync (display->xdisplay, False);
}
2001-05-30 23:30:58 -04:00
MetaDisplay*
meta_display_for_x_display (Display *xdisplay)
{
GSList *tmp;
tmp = all_displays;
while (tmp != NULL)
{
MetaDisplay *display = tmp->data;
if (display->xdisplay == xdisplay)
return display;
tmp = tmp->next;
}
return NULL;
}
2001-05-31 02:42:58 -04:00
GSList*
meta_displays_list (void)
{
return all_displays;
}
2001-05-30 11:36:31 -04:00
static gboolean dump_events = TRUE;
static void
event_queue_callback (MetaEventQueue *queue,
XEvent *event,
gpointer data)
{
MetaWindow *window;
MetaDisplay *display;
2001-06-02 21:33:27 -04:00
Window modified;
2001-05-31 02:42:58 -04:00
2001-05-30 11:36:31 -04:00
display = data;
if (dump_events)
meta_spew_event (display, event);
2001-06-02 21:33:27 -04:00
modified = event_get_modified_window (display, event);
if (modified != None)
window = meta_display_lookup_x_window (display, modified);
else
window = NULL;
2001-05-31 02:42:58 -04:00
2001-06-02 21:33:27 -04:00
if (window &&
window->frame &&
modified == window->frame->xwindow)
2001-05-30 11:36:31 -04:00
{
2001-06-02 21:33:27 -04:00
meta_frame_event (window->frame, event);
return;
2001-05-30 11:36:31 -04:00
}
switch (event->type)
{
case KeyPress:
2001-06-06 00:47:37 -04:00
meta_display_process_key_press (display, event);
2001-05-30 11:36:31 -04:00
break;
case KeyRelease:
break;
case ButtonPress:
break;
case ButtonRelease:
break;
case MotionNotify:
break;
case EnterNotify:
2001-06-04 00:58:22 -04:00
/* We handle it here if an undecorated window
* is involved, otherwise we handle it in frame.c
*/
/* do this even if window->has_focus to avoid races */
if (window)
meta_window_focus (window, event->xcrossing.time);
2001-05-30 11:36:31 -04:00
break;
case LeaveNotify:
break;
case FocusIn:
2001-06-04 00:58:22 -04:00
if (window)
{
if (window != window->display->focus_window)
window->display->focus_window = window;
window->has_focus = TRUE;
if (window->frame)
meta_frame_queue_draw (window->frame);
}
2001-05-30 11:36:31 -04:00
break;
case FocusOut:
2001-06-04 00:58:22 -04:00
if (window)
{
if (window == window->display->focus_window)
window->display->focus_window = NULL;
window->has_focus = FALSE;
if (window->frame)
meta_frame_queue_draw (window->frame);
}
2001-05-30 11:36:31 -04:00
break;
case KeymapNotify:
break;
case Expose:
break;
case GraphicsExpose:
break;
case NoExpose:
break;
case VisibilityNotify:
break;
case CreateNotify:
break;
case DestroyNotify:
2001-06-02 21:33:27 -04:00
if (window)
meta_window_free (window); /* Unmanage destroyed window */
2001-05-30 11:36:31 -04:00
break;
case UnmapNotify:
2001-06-04 02:17:52 -04:00
if (window && window->mapped)
{
meta_verbose ("Window %s withdrawn\n",
window->desc);
meta_window_free (window); /* Unmanage withdrawn window */
}
2001-05-30 11:36:31 -04:00
break;
case MapNotify:
2001-06-04 02:17:52 -04:00
if (window)
window->mapped = TRUE;
2001-05-30 11:36:31 -04:00
break;
case MapRequest:
2001-06-02 21:33:27 -04:00
if (window == NULL)
window = meta_window_new (display, event->xmaprequest.window);
2001-05-30 11:36:31 -04:00
break;
case ReparentNotify:
break;
case ConfigureNotify:
break;
case ConfigureRequest:
2001-06-02 21:33:27 -04:00
/* 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);
}
2001-05-30 11:36:31 -04:00
break;
case GravityNotify:
break;
case ResizeRequest:
break;
case CirculateNotify:
break;
case CirculateRequest:
break;
case PropertyNotify:
2001-06-02 21:33:27 -04:00
if (window)
meta_window_property_notify (window, event);
2001-05-30 11:36:31 -04:00
break;
case SelectionClear:
break;
case SelectionRequest:
break;
case SelectionNotify:
break;
case ColormapNotify:
break;
case ClientMessage:
break;
case MappingNotify:
break;
default:
break;
2001-06-02 21:33:27 -04:00
}
2001-05-30 11:36:31 -04:00
}
2001-06-02 21:33:27 -04:00
/* 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 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:
2001-06-04 00:58:22 -04:00
case EnterNotify:
case LeaveNotify:
2001-06-02 21:33:27 -04:00
return event->xany.window;
2001-06-04 00:58:22 -04:00
2001-06-02 21:33:27 -04:00
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;
}
}
2001-05-30 11:36:31 -04:00
static void
meta_spew_event (MetaDisplay *display,
XEvent *event)
{
const char *name = NULL;
char *extra = NULL;
char *winname;
MetaScreen *screen;
switch (event->type)
{
case KeyPress:
name = "KeyPress";
break;
case KeyRelease:
name = "KeyRelease";
break;
case ButtonPress:
name = "ButtonPress";
break;
case ButtonRelease:
name = "ButtonRelease";
break;
case MotionNotify:
name = "MotionNotify";
break;
case EnterNotify:
name = "EnterNotify";
2001-06-04 00:58:22 -04:00
extra = g_strdup_printf ("win: 0x%lx root: 0x%lx subwindow: 0x%lx mode: %d detail: %d\n",
event->xcrossing.window,
event->xcrossing.root,
event->xcrossing.subwindow,
event->xcrossing.mode,
event->xcrossing.detail);
2001-05-30 11:36:31 -04:00
break;
case LeaveNotify:
name = "LeaveNotify";
2001-06-04 00:58:22 -04:00
extra = g_strdup_printf ("win: 0x%lx root: 0x%lx subwindow: 0x%lx mode: %d detail: %d\n",
event->xcrossing.window,
event->xcrossing.root,
event->xcrossing.subwindow,
event->xcrossing.mode,
event->xcrossing.detail);
2001-05-30 11:36:31 -04:00
break;
case FocusIn:
name = "FocusIn";
break;
case FocusOut:
name = "FocusOut";
break;
case KeymapNotify:
name = "KeymapNotify";
break;
case Expose:
name = "Expose";
break;
case GraphicsExpose:
name = "GraphicsExpose";
break;
case NoExpose:
name = "NoExpose";
break;
case VisibilityNotify:
name = "VisibilityNotify";
break;
case CreateNotify:
name = "CreateNotify";
break;
case DestroyNotify:
name = "DestroyNotify";
break;
case UnmapNotify:
name = "UnmapNotify";
break;
case MapNotify:
name = "MapNotify";
break;
case MapRequest:
name = "MapRequest";
break;
case ReparentNotify:
name = "ReparentNotify";
break;
case ConfigureNotify:
name = "ConfigureNotify";
extra = g_strdup_printf ("x: %d y: %d w: %d h: %d above: 0x%lx",
event->xconfigure.x,
event->xconfigure.y,
event->xconfigure.width,
event->xconfigure.height,
event->xconfigure.above);
break;
case ConfigureRequest:
name = "ConfigureRequest";
2001-06-02 21:33:27 -04:00
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);
2001-05-30 11:36:31 -04:00
break;
case GravityNotify:
name = "GravityNotify";
break;
case ResizeRequest:
name = "ResizeRequest";
break;
case CirculateNotify:
name = "CirculateNotify";
break;
case CirculateRequest:
name = "CirculateRequest";
break;
case PropertyNotify:
name = "PropertyNotify";
break;
case SelectionClear:
name = "SelectionClear";
break;
case SelectionRequest:
name = "SelectionRequest";
break;
case SelectionNotify:
name = "SelectionNotify";
break;
case ColormapNotify:
name = "ColormapNotify";
break;
case ClientMessage:
name = "ClientMessage";
break;
case MappingNotify:
name = "MappingNotify";
break;
default:
name = "Unknown";
break;
}
screen = meta_display_screen_for_root (display, event->xany.window);
if (screen)
winname = g_strdup_printf ("root %d", screen->number);
else
winname = g_strdup_printf ("0x%lx", event->xany.window);
meta_verbose ("%s on %s%s %s\n", name, winname,
extra ? ":" : "", extra ? extra : "");
g_free (winname);
if (extra)
g_free (extra);
}
MetaWindow*
2001-05-31 02:42:58 -04:00
meta_display_lookup_x_window (MetaDisplay *display,
Window xwindow)
2001-05-30 11:36:31 -04:00
{
return g_hash_table_lookup (display->window_ids, &xwindow);
}
void
2001-05-31 02:42:58 -04:00
meta_display_register_x_window (MetaDisplay *display,
Window *xwindowp,
MetaWindow *window)
2001-05-30 11:36:31 -04:00
{
2001-05-31 02:42:58 -04:00
g_return_if_fail (g_hash_table_lookup (display->window_ids, xwindowp) == NULL);
2001-05-30 11:36:31 -04:00
2001-05-31 02:42:58 -04:00
g_hash_table_insert (display->window_ids, xwindowp, window);
}
void
meta_display_unregister_x_window (MetaDisplay *display,
Window xwindow)
{
g_return_if_fail (g_hash_table_lookup (display->window_ids, &xwindow) != NULL);
g_hash_table_remove (display->window_ids, &xwindow);
2001-05-30 11:36:31 -04:00
}
2001-06-06 00:47:37 -04:00
MetaWorkspace*
meta_display_get_workspace_by_index (MetaDisplay *display,
int index)
{
GList *tmp;
tmp = g_list_nth (display->workspaces, index);
if (tmp == NULL)
return NULL;
else
return tmp->data;
}
MetaWorkspace*
meta_display_get_workspace_by_screen_index (MetaDisplay *display,
MetaScreen *screen,
int index)
{
GList *tmp;
int i;
i = 0;
tmp = display->workspaces;
while (tmp != NULL)
{
MetaWorkspace *w = tmp->data;
if (w->screen == screen)
{
if (i == index)
return w;
else
++i;
}
tmp = tmp->next;
}
return NULL;
}